/*!
  * vue-router v4.0.15
  * (c) 2022 Eduardo San Martin Morote
  * @license MIT
  */
var VueRouter = (function (exports, vue) {
    'use strict';

    const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
    const PolySymbol = (name) =>
        // vr = vue router
        hasSymbol
            ? Symbol('[vue-router]: ' + name )
            : ('[vue-router]: ' ) + name;
    // rvlm = Router View Location Matched
    /**
     * RouteRecord being rendered by the closest ancestor Router View. Used for
     * `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View
     * Location Matched
     *
     * @internal
     */
    const matchedRouteKey = /*#__PURE__*/ PolySymbol('router view location matched' );
    /**
     * Allows overriding the router view depth to control which component in
     * `matched` is rendered. rvd stands for Router View Depth
     *
     * @internal
     */
    const viewDepthKey = /*#__PURE__*/ PolySymbol('router view depth' );
    /**
     * Allows overriding the router instance returned by `useRouter` in tests. r
     * stands for router
     *
     * @internal
     */
    const routerKey = /*#__PURE__*/ PolySymbol('router' );
    /**
     * Allows overriding the current route returned by `useRoute` in tests. rl
     * stands for route location
     *
     * @internal
     */
    const routeLocationKey = /*#__PURE__*/ PolySymbol('route location' );
    /**
     * Allows overriding the current route used by router-view. Internally this is
     * used when the `route` prop is passed.
     *
     * @internal
     */
    const routerViewLocationKey = /*#__PURE__*/ PolySymbol('router view location' );

    const isBrowser = typeof window !== 'undefined';

    function isESModule(obj) {
        return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module');
    }
    const assign = Object.assign;
    function applyToParams(fn, params) {
        const newParams = {};
        for (const key in params) {
            const value = params[key];
            newParams[key] = Array.isArray(value) ? value.map(fn) : fn(value);
        }
        return newParams;
    }
    const noop = () => { };

    function warn(msg) {
        // avoid using ...args as it breaks in older Edge builds
        const args = Array.from(arguments).slice(1);
        console.warn.apply(console, ['[Vue Router warn]: ' + msg].concat(args));
    }

    const TRAILING_SLASH_RE = /\/$/;
    const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, '');
    /**
     * Transforms an URI into a normalized history location
     *
     * @param parseQuery
     * @param location - URI to normalize
     * @param currentLocation - current absolute location. Allows resolving relative
     * paths. Must start with `/`. Defaults to `/`
     * @returns a normalized history location
     */
    function parseURL(parseQuery, location, currentLocation = '/') {
        let path, query = {}, searchString = '', hash = '';
        // Could use URL and URLSearchParams but IE 11 doesn't support it
        const searchPos = location.indexOf('?');
        const hashPos = location.indexOf('#', searchPos > -1 ? searchPos : 0);
        if (searchPos > -1) {
            path = location.slice(0, searchPos);
            searchString = location.slice(searchPos + 1, hashPos > -1 ? hashPos : location.length);
            query = parseQuery(searchString);
        }
        if (hashPos > -1) {
            path = path || location.slice(0, hashPos);
            // keep the # character
            hash = location.slice(hashPos, location.length);
        }
        // no search and no query
        path = resolveRelativePath(path != null ? path : location, currentLocation);
        // empty path means a relative query or hash `?foo=f`, `#thing`
        return {
            fullPath: path + (searchString && '?') + searchString + hash,
            path,
            query,
            hash,
        };
    }
    /**
     * Stringifies a URL object
     *
     * @param stringifyQuery
     * @param location
     */
    function stringifyURL(stringifyQuery, location) {
        const query = location.query ? stringifyQuery(location.query) : '';
        return location.path + (query && '?') + query + (location.hash || '');
    }
    /**
     * Strips off the base from the beginning of a location.pathname in a non
     * case-sensitive way.
     *
     * @param pathname - location.pathname
     * @param base - base to strip off
     */
    function stripBase(pathname, base) {
        // no base or base is not found at the beginning
        if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase()))
            return pathname;
        return pathname.slice(base.length) || '/';
    }
    /**
     * Checks if two RouteLocation are equal. This means that both locations are
     * pointing towards the same {@link RouteRecord} and that all `params`, `query`
     * parameters and `hash` are the same
     *
     * @param a - first {@link RouteLocation}
     * @param b - second {@link RouteLocation}
     */
    function isSameRouteLocation(stringifyQuery, a, b) {
        const aLastIndex = a.matched.length - 1;
        const bLastIndex = b.matched.length - 1;
        return (aLastIndex > -1 &&
            aLastIndex === bLastIndex &&
            isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) &&
            isSameRouteLocationParams(a.params, b.params) &&
            stringifyQuery(a.query) === stringifyQuery(b.query) &&
            a.hash === b.hash);
    }
    /**
     * Check if two `RouteRecords` are equal. Takes into account aliases: they are
     * considered equal to the `RouteRecord` they are aliasing.
     *
     * @param a - first {@link RouteRecord}
     * @param b - second {@link RouteRecord}
     */
    function isSameRouteRecord(a, b) {
        // since the original record has an undefined value for aliasOf
        // but all aliases point to the original record, this will always compare
        // the original record
        return (a.aliasOf || a) === (b.aliasOf || b);
    }
    function isSameRouteLocationParams(a, b) {
        if (Object.keys(a).length !== Object.keys(b).length)
            return false;
        for (const key in a) {
            if (!isSameRouteLocationParamsValue(a[key], b[key]))
                return false;
        }
        return true;
    }
    function isSameRouteLocationParamsValue(a, b) {
        return Array.isArray(a)
            ? isEquivalentArray(a, b)
            : Array.isArray(b)
                ? isEquivalentArray(b, a)
                : a === b;
    }
    /**
     * Check if two arrays are the same or if an array with one single entry is the
     * same as another primitive value. Used to check query and parameters
     *
     * @param a - array of values
     * @param b - array of values or a single value
     */
    function isEquivalentArray(a, b) {
        return Array.isArray(b)
            ? a.length === b.length && a.every((value, i) => value === b[i])
            : a.length === 1 && a[0] === b;
    }
    /**
     * Resolves a relative path that starts with `.`.
     *
     * @param to - path location we are resolving
     * @param from - currentLocation.path, should start with `/`
     */
    function resolveRelativePath(to, from) {
        if (to.startsWith('/'))
            return to;
        if (!from.startsWith('/')) {
            warn(`Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".`);
            return to;
        }
        if (!to)
            return from;
        const fromSegments = from.split('/');
        const toSegments = to.split('/');
        let position = fromSegments.length - 1;
        let toPosition;
        let segment;
        for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
            segment = toSegments[toPosition];
            // can't go below zero
            if (position === 1 || segment === '.')
                continue;
            if (segment === '..')
                position--;
            // found something that is not relative path
            else
                break;
        }
        return (fromSegments.slice(0, position).join('/') +
            '/' +
            toSegments
                .slice(toPosition - (toPosition === toSegments.length ? 1 : 0))
                .join('/'));
    }

    var NavigationType;
    (function (NavigationType) {
        NavigationType["pop"] = "pop";
        NavigationType["push"] = "push";
    })(NavigationType || (NavigationType = {}));
    var NavigationDirection;
    (function (NavigationDirection) {
        NavigationDirection["back"] = "back";
        NavigationDirection["forward"] = "forward";
        NavigationDirection["unknown"] = "";
    })(NavigationDirection || (NavigationDirection = {}));
    /**
     * Starting location for Histories
     */
    const START = '';
    // Generic utils
    /**
     * Normalizes a base by removing any trailing slash and reading the base tag if
     * present.
     *
     * @param base - base to normalize
     */
    function normalizeBase(base) {
        if (!base) {
            if (isBrowser) {
                // respect <base> tag
                const baseEl = document.querySelector('base');
                base = (baseEl && baseEl.getAttribute('href')) || '/';
                // strip full URL origin
                base = base.replace(/^\w+:\/\/[^\/]+/, '');
            }
            else {
                base = '/';
            }
        }
        // ensure leading slash when it was removed by the regex above avoid leading
        // slash with hash because the file could be read from the disk like file://
        // and the leading slash would cause problems
        if (base[0] !== '/' && base[0] !== '#')
            base = '/' + base;
        // remove the trailing slash so all other method can just do `base + fullPath`
        // to build an href
        return removeTrailingSlash(base);
    }
    // remove any character before the hash
    const BEFORE_HASH_RE = /^[^#]+#/;
    function createHref(base, location) {
        return base.replace(BEFORE_HASH_RE, '#') + location;
    }

    function getElementPosition(el, offset) {
        const docRect = document.documentElement.getBoundingClientRect();
        const elRect = el.getBoundingClientRect();
        return {
            behavior: offset.behavior,
            left: elRect.left - docRect.left - (offset.left || 0),
            top: elRect.top - docRect.top - (offset.top || 0),
        };
    }
    const computeScrollPosition = () => ({
        left: window.pageXOffset,
        top: window.pageYOffset,
    });
    function scrollToPosition(position) {
        let scrollToOptions;
        if ('el' in position) {
            const positionEl = position.el;
            const isIdSelector = typeof positionEl === 'string' && positionEl.startsWith('#');
            /**
             * `id`s can accept pretty much any characters, including CSS combinators
             * like `>` or `~`. It's still possible to retrieve elements using
             * `document.getElementById('~')` but it needs to be escaped when using
             * `document.querySelector('#\\~')` for it to be valid. The only
             * requirements for `id`s are them to be unique on the page and to not be
             * empty (`id=""`). Because of that, when passing an id selector, it should
             * be properly escaped for it to work with `querySelector`. We could check
             * for the id selector to be simple (no CSS combinators `+ >~`) but that
             * would make things inconsistent since they are valid characters for an
             * `id` but would need to be escaped when using `querySelector`, breaking
             * their usage and ending up in no selector returned. Selectors need to be
             * escaped:
             *
             * - `#1-thing` becomes `#\31 -thing`
             * - `#with~symbols` becomes `#with\\~symbols`
             *
             * - More information about  the topic can be found at
             *   https://mathiasbynens.be/notes/html5-id-class.
             * - Practical example: https://mathiasbynens.be/demo/html5-id
             */
            if (typeof position.el === 'string') {
                if (!isIdSelector || !document.getElementById(position.el.slice(1))) {
                    try {
                        const foundEl = document.querySelector(position.el);
                        if (isIdSelector && foundEl) {
                            warn(`The selector "${position.el}" should be passed as "el: document.querySelector('${position.el}')" because it starts with "#".`);
                            // return to avoid other warnings
                            return;
                        }
                    }
                    catch (err) {
                        warn(`The selector "${position.el}" is invalid. If you are using an id selector, make sure to escape it. You can find more information about escaping characters in selectors at https://mathiasbynens.be/notes/css-escapes or use CSS.escape (https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape).`);
                        // return to avoid other warnings
                        return;
                    }
                }
            }
            const el = typeof positionEl === 'string'
                ? isIdSelector
                    ? document.getElementById(positionEl.slice(1))
                    : document.querySelector(positionEl)
                : positionEl;
            if (!el) {
                warn(`Couldn't find element using selector "${position.el}" returned by scrollBehavior.`);
                return;
            }
            scrollToOptions = getElementPosition(el, position);
        }
        else {
            scrollToOptions = position;
        }
        if ('scrollBehavior' in document.documentElement.style)
            window.scrollTo(scrollToOptions);
        else {
            window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.pageXOffset, scrollToOptions.top != null ? scrollToOptions.top : window.pageYOffset);
        }
    }
    function getScrollKey(path, delta) {
        const position = history.state ? history.state.position - delta : -1;
        return position + path;
    }
    const scrollPositions = new Map();
    function saveScrollPosition(key, scrollPosition) {
        scrollPositions.set(key, scrollPosition);
    }
    function getSavedScrollPosition(key) {
        const scroll = scrollPositions.get(key);
        // consume it so it's not used again
        scrollPositions.delete(key);
        return scroll;
    }
    // TODO: RFC about how to save scroll position
    /**
     * ScrollBehavior instance used by the router to compute and restore the scroll
     * position when navigating.
     */
        // export interface ScrollHandler<ScrollPositionEntry extends HistoryStateValue, ScrollPosition extends ScrollPositionEntry> {
        //   // returns a scroll position that can be saved in history
        //   compute(): ScrollPositionEntry
        //   // can take an extended ScrollPositionEntry
        //   scroll(position: ScrollPosition): void
        // }
        // export const scrollHandler: ScrollHandler<ScrollPosition> = {
        //   compute: computeScroll,
        //   scroll: scrollToPosition,
        // }

    let createBaseLocation = () => location.protocol + '//' + location.host;
    /**
     * Creates a normalized history location from a window.location object
     * @param location -
     */
    function createCurrentLocation(base, location) {
        const { pathname, search, hash } = location;
        // allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end
        const hashPos = base.indexOf('#');
        if (hashPos > -1) {
            let slicePos = hash.includes(base.slice(hashPos))
                ? base.slice(hashPos).length
                : 1;
            let pathFromHash = hash.slice(slicePos);
            // prepend the starting slash to hash so the url starts with /#
            if (pathFromHash[0] !== '/')
                pathFromHash = '/' + pathFromHash;
            return stripBase(pathFromHash, '');
        }
        const path = stripBase(pathname, base);
        return path + search + hash;
    }
    function useHistoryListeners(base, historyState, currentLocation, replace) {
        let listeners = [];
        let teardowns = [];
        // TODO: should it be a stack? a Dict. Check if the popstate listener
        // can trigger twice
        let pauseState = null;
        const popStateHandler = ({ state, }) => {
            const to = createCurrentLocation(base, location);
            const from = currentLocation.value;
            const fromState = historyState.value;
            let delta = 0;
            if (state) {
                currentLocation.value = to;
                historyState.value = state;
                // ignore the popstate and reset the pauseState
                if (pauseState && pauseState === from) {
                    pauseState = null;
                    return;
                }
                delta = fromState ? state.position - fromState.position : 0;
            }
            else {
                replace(to);
            }
            // console.log({ deltaFromCurrent })
            // Here we could also revert the navigation by calling history.go(-delta)
            // this listener will have to be adapted to not trigger again and to wait for the url
            // to be updated before triggering the listeners. Some kind of validation function would also
            // need to be passed to the listeners so the navigation can be accepted
            // call all listeners
            listeners.forEach(listener => {
                listener(currentLocation.value, from, {
                    delta,
                    type: NavigationType.pop,
                    direction: delta
                        ? delta > 0
                            ? NavigationDirection.forward
                            : NavigationDirection.back
                        : NavigationDirection.unknown,
                });
            });
        };
        function pauseListeners() {
            pauseState = currentLocation.value;
        }
        function listen(callback) {
            // setup the listener and prepare teardown callbacks
            listeners.push(callback);
            const teardown = () => {
                const index = listeners.indexOf(callback);
                if (index > -1)
                    listeners.splice(index, 1);
            };
            teardowns.push(teardown);
            return teardown;
        }
        function beforeUnloadListener() {
            const { history } = window;
            if (!history.state)
                return;
            history.replaceState(assign({}, history.state, { scroll: computeScrollPosition() }), '');
        }
        function destroy() {
            for (const teardown of teardowns)
                teardown();
            teardowns = [];
            window.removeEventListener('popstate', popStateHandler);
            window.removeEventListener('beforeunload', beforeUnloadListener);
        }
        // setup the listeners and prepare teardown callbacks
        window.addEventListener('popstate', popStateHandler);
        window.addEventListener('beforeunload', beforeUnloadListener);
        return {
            pauseListeners,
            listen,
            destroy,
        };
    }
    /**
     * Creates a state object
     */
    function buildState(back, current, forward, replaced = false, computeScroll = false) {
        return {
            back,
            current,
            forward,
            replaced,
            position: window.history.length,
            scroll: computeScroll ? computeScrollPosition() : null,
        };
    }
    function useHistoryStateNavigation(base) {
        const { history, location } = window;
        // private variables
        const currentLocation = {
            value: createCurrentLocation(base, location),
        };
        const historyState = { value: history.state };
        // build current history entry as this is a fresh navigation
        if (!historyState.value) {
            changeLocation(currentLocation.value, {
                back: null,
                current: currentLocation.value,
                forward: null,
                // the length is off by one, we need to decrease it
                position: history.length - 1,
                replaced: true,
                // don't add a scroll as the user may have an anchor and we want
                // scrollBehavior to be triggered without a saved position
                scroll: null,
            }, true);
        }
        function changeLocation(to, state, replace) {
            /**
             * if a base tag is provided and we are on a normal domain, we have to
             * respect the provided `base` attribute because pushState() will use it and
             * potentially erase anything before the `#` like at
             * https://github.com/vuejs/router/issues/685 where a base of
             * `/folder/#` but a base of `/` would erase the `/folder/` section. If
             * there is no host, the `<base>` tag makes no sense and if there isn't a
             * base tag we can just use everything after the `#`.
             */
            const hashIndex = base.indexOf('#');
            const url = hashIndex > -1
                ? (location.host && document.querySelector('base')
                ? base
                : base.slice(hashIndex)) + to
                : createBaseLocation() + base + to;
            try {
                // BROWSER QUIRK
                // NOTE: Safari throws a SecurityError when calling this function 100 times in 30 seconds
                history[replace ? 'replaceState' : 'pushState'](state, '', url);
                historyState.value = state;
            }
            catch (err) {
                {
                    warn('Error with push/replace State', err);
                }
                // Force the navigation, this also resets the call count
                location[replace ? 'replace' : 'assign'](url);
            }
        }
        function replace(to, data) {
            const state = assign({}, history.state, buildState(historyState.value.back,
                // keep back and forward entries but override current position
                to, historyState.value.forward, true), data, { position: historyState.value.position });
            changeLocation(to, state, true);
            currentLocation.value = to;
        }
        function push(to, data) {
            // Add to current entry the information of where we are going
            // as well as saving the current position
            const currentState = assign({},
                // use current history state to gracefully handle a wrong call to
                // history.replaceState
                // https://github.com/vuejs/router/issues/366
                historyState.value, history.state, {
                    forward: to,
                    scroll: computeScrollPosition(),
                });
            if (!history.state) {
                warn(`history.state seems to have been manually replaced without preserving the necessary values. Make sure to preserve existing history state if you are manually calling history.replaceState:\n\n` +
                    `history.replaceState(history.state, '', url)\n\n` +
                    `You can find more information at https://next.router.vuejs.org/guide/migration/#usage-of-history-state.`);
            }
            changeLocation(currentState.current, currentState, true);
            const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data);
            changeLocation(to, state, false);
            currentLocation.value = to;
        }
        return {
            location: currentLocation,
            state: historyState,
            push,
            replace,
        };
    }
    /**
     * Creates an HTML5 history. Most common history for single page applications.
     *
     * @param base -
     */
    function createWebHistory(base) {
        base = normalizeBase(base);
        const historyNavigation = useHistoryStateNavigation(base);
        const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location, historyNavigation.replace);
        function go(delta, triggerListeners = true) {
            if (!triggerListeners)
                historyListeners.pauseListeners();
            history.go(delta);
        }
        const routerHistory = assign({
            // it's overridden right after
            location: '',
            base,
            go,
            createHref: createHref.bind(null, base),
        }, historyNavigation, historyListeners);
        Object.defineProperty(routerHistory, 'location', {
            enumerable: true,
            get: () => historyNavigation.location.value,
        });
        Object.defineProperty(routerHistory, 'state', {
            enumerable: true,
            get: () => historyNavigation.state.value,
        });
        return routerHistory;
    }

    /**
     * Creates a in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere.
     * It's up to the user to replace that location with the starter location by either calling `router.push` or `router.replace`.
     *
     * @param base - Base applied to all urls, defaults to '/'
     * @returns a history object that can be passed to the router constructor
     */
    function createMemoryHistory(base = '') {
        let listeners = [];
        let queue = [START];
        let position = 0;
        base = normalizeBase(base);
        function setLocation(location) {
            position++;
            if (position === queue.length) {
                // we are at the end, we can simply append a new entry
                queue.push(location);
            }
            else {
                // we are in the middle, we remove everything from here in the queue
                queue.splice(position);
                queue.push(location);
            }
        }
        function triggerListeners(to, from, { direction, delta }) {
            const info = {
                direction,
                delta,
                type: NavigationType.pop,
            };
            for (const callback of listeners) {
                callback(to, from, info);
            }
        }
        const routerHistory = {
            // rewritten by Object.defineProperty
            location: START,
            // TODO: should be kept in queue
            state: {},
            base,
            createHref: createHref.bind(null, base),
            replace(to) {
                // remove current entry and decrement position
                queue.splice(position--, 1);
                setLocation(to);
            },
            push(to, data) {
                setLocation(to);
            },
            listen(callback) {
                listeners.push(callback);
                return () => {
                    const index = listeners.indexOf(callback);
                    if (index > -1)
                        listeners.splice(index, 1);
                };
            },
            destroy() {
                listeners = [];
                queue = [START];
                position = 0;
            },
            go(delta, shouldTrigger = true) {
                const from = this.location;
                const direction =
                    // we are considering delta === 0 going forward, but in abstract mode
                    // using 0 for the delta doesn't make sense like it does in html5 where
                    // it reloads the page
                    delta < 0 ? NavigationDirection.back : NavigationDirection.forward;
                position = Math.max(0, Math.min(position + delta, queue.length - 1));
                if (shouldTrigger) {
                    triggerListeners(this.location, from, {
                        direction,
                        delta,
                    });
                }
            },
        };
        Object.defineProperty(routerHistory, 'location', {
            enumerable: true,
            get: () => queue[position],
        });
        return routerHistory;
    }

    /**
     * Creates a hash history. Useful for web applications with no host (e.g.
     * `file://`) or when configuring a server to handle any URL is not possible.
     *
     * @param base - optional base to provide. Defaults to `location.pathname +
     * location.search` If there is a `<base>` tag in the `head`, its value will be
     * ignored in favor of this parameter **but note it affects all the
     * history.pushState() calls**, meaning that if you use a `<base>` tag, it's
     * `href` value **has to match this parameter** (ignoring anything after the
     * `#`).
     *
     * @example
     * ```js
     * // at https://example.com/folder
     * createWebHashHistory() // gives a url of `https://example.com/folder#`
     * createWebHashHistory('/folder/') // gives a url of `https://example.com/folder/#`
     * // if the `#` is provided in the base, it won't be added by `createWebHashHistory`
     * createWebHashHistory('/folder/#/app/') // gives a url of `https://example.com/folder/#/app/`
     * // you should avoid doing this because it changes the original url and breaks copying urls
     * createWebHashHistory('/other-folder/') // gives a url of `https://example.com/other-folder/#`
     *
     * // at file:///usr/etc/folder/index.html
     * // for locations with no `host`, the base is ignored
     * createWebHashHistory('/iAmIgnored') // gives a url of `file:///usr/etc/folder/index.html#`
     * ```
     */
    function createWebHashHistory(base) {
        // Make sure this implementation is fine in terms of encoding, specially for IE11
        // for `file://`, directly use the pathname and ignore the base
        // location.pathname contains an initial `/` even at the root: `https://example.com`
        base = location.host ? base || location.pathname + location.search : '';
        // allow the user to provide a `#` in the middle: `/base/#/app`
        if (!base.includes('#'))
            base += '#';
        if (!base.endsWith('#/') && !base.endsWith('#')) {
            warn(`A hash base must end with a "#":\n"${base}" should be "${base.replace(/#.*$/, '#')}".`);
        }
        return createWebHistory(base);
    }

    function isRouteLocation(route) {
        return typeof route === 'string' || (route && typeof route === 'object');
    }
    function isRouteName(name) {
        return typeof name === 'string' || typeof name === 'symbol';
    }

    /**
     * Initial route location where the router is. Can be used in navigation guards
     * to differentiate the initial navigation.
     *
     * @example
     * ```js
     * import { START_LOCATION } from 'vue-router'
     *
     * router.beforeEach((to, from) => {
     *   if (from === START_LOCATION) {
     *     // initial navigation
     *   }
     * })
     * ```
     */
    const START_LOCATION_NORMALIZED = {
        path: '/',
        name: undefined,
        params: {},
        query: {},
        hash: '',
        fullPath: '/',
        matched: [],
        meta: {},
        redirectedFrom: undefined,
    };

    const NavigationFailureSymbol = /*#__PURE__*/ PolySymbol('navigation failure' );
    /**
     * Enumeration with all possible types for navigation failures. Can be passed to
     * {@link isNavigationFailure} to check for specific failures.
     */
    exports.NavigationFailureType = void 0;
    (function (NavigationFailureType) {
        /**
         * An aborted navigation is a navigation that failed because a navigation
         * guard returned `false` or called `next(false)`
         */
        NavigationFailureType[NavigationFailureType["aborted"] = 4] = "aborted";
        /**
         * A cancelled navigation is a navigation that failed because a more recent
         * navigation finished started (not necessarily finished).
         */
        NavigationFailureType[NavigationFailureType["cancelled"] = 8] = "cancelled";
        /**
         * A duplicated navigation is a navigation that failed because it was
         * initiated while already being at the exact same location.
         */
        NavigationFailureType[NavigationFailureType["duplicated"] = 16] = "duplicated";
    })(exports.NavigationFailureType || (exports.NavigationFailureType = {}));
    // DEV only debug messages
    const ErrorTypeMessages = {
        [1 /* MATCHER_NOT_FOUND */]({ location, currentLocation }) {
            return `No match for\n ${JSON.stringify(location)}${currentLocation
                ? '\nwhile being at\n' + JSON.stringify(currentLocation)
                : ''}`;
        },
        [2 /* NAVIGATION_GUARD_REDIRECT */]({ from, to, }) {
            return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`;
        },
        [4 /* NAVIGATION_ABORTED */]({ from, to }) {
            return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`;
        },
        [8 /* NAVIGATION_CANCELLED */]({ from, to }) {
            return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`;
        },
        [16 /* NAVIGATION_DUPLICATED */]({ from, to }) {
            return `Avoided redundant navigation to current location: "${from.fullPath}".`;
        },
    };
    function createRouterError(type, params) {
        // keep full error messages in cjs versions
        {
            return assign(new Error(ErrorTypeMessages[type](params)), {
                type,
                [NavigationFailureSymbol]: true,
            }, params);
        }
    }
    function isNavigationFailure(error, type) {
        return (error instanceof Error &&
            NavigationFailureSymbol in error &&
            (type == null || !!(error.type & type)));
    }
    const propertiesToLog = ['params', 'query', 'hash'];
    function stringifyRoute(to) {
        if (typeof to === 'string')
            return to;
        if ('path' in to)
            return to.path;
        const location = {};
        for (const key of propertiesToLog) {
            if (key in to)
                location[key] = to[key];
        }
        return JSON.stringify(location, null, 2);
    }

    // default pattern for a param: non greedy everything but /
    const BASE_PARAM_PATTERN = '[^/]+?';
    const BASE_PATH_PARSER_OPTIONS = {
        sensitive: false,
        strict: false,
        start: true,
        end: true,
    };
    // Special Regex characters that must be escaped in static tokens
    const REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g;
    /**
     * Creates a path parser from an array of Segments (a segment is an array of Tokens)
     *
     * @param segments - array of segments returned by tokenizePath
     * @param extraOptions - optional options for the regexp
     * @returns a PathParser
     */
    function tokensToParser(segments, extraOptions) {
        const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions);
        // the amount of scores is the same as the length of segments except for the root segment "/"
        const score = [];
        // the regexp as a string
        let pattern = options.start ? '^' : '';
        // extracted keys
        const keys = [];
        for (const segment of segments) {
            // the root segment needs special treatment
            const segmentScores = segment.length ? [] : [90 /* Root */];
            // allow trailing slash
            if (options.strict && !segment.length)
                pattern += '/';
            for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
                const token = segment[tokenIndex];
                // resets the score if we are inside a sub segment /:a-other-:b
                let subSegmentScore = 40 /* Segment */ +
                    (options.sensitive ? 0.25 /* BonusCaseSensitive */ : 0);
                if (token.type === 0 /* Static */) {
                    // prepend the slash if we are starting a new segment
                    if (!tokenIndex)
                        pattern += '/';
                    pattern += token.value.replace(REGEX_CHARS_RE, '\\$&');
                    subSegmentScore += 40 /* Static */;
                }
                else if (token.type === 1 /* Param */) {
                    const { value, repeatable, optional, regexp } = token;
                    keys.push({
                        name: value,
                        repeatable,
                        optional,
                    });
                    const re = regexp ? regexp : BASE_PARAM_PATTERN;
                    // the user provided a custom regexp /:id(\\d+)
                    if (re !== BASE_PARAM_PATTERN) {
                        subSegmentScore += 10 /* BonusCustomRegExp */;
                        // make sure the regexp is valid before using it
                        try {
                            new RegExp(`(${re})`);
                        }
                        catch (err) {
                            throw new Error(`Invalid custom RegExp for param "${value}" (${re}): ` +
                                err.message);
                        }
                    }
                    // when we repeat we must take care of the repeating leading slash
                    let subPattern = repeatable ? `((?:${re})(?:/(?:${re}))*)` : `(${re})`;
                    // prepend the slash if we are starting a new segment
                    if (!tokenIndex)
                        subPattern =
                            // avoid an optional / if there are more segments e.g. /:p?-static
                            // or /:p?-:p2
                            optional && segment.length < 2
                                ? `(?:/${subPattern})`
                                : '/' + subPattern;
                    if (optional)
                        subPattern += '?';
                    pattern += subPattern;
                    subSegmentScore += 20 /* Dynamic */;
                    if (optional)
                        subSegmentScore += -8 /* BonusOptional */;
                    if (repeatable)
                        subSegmentScore += -20 /* BonusRepeatable */;
                    if (re === '.*')
                        subSegmentScore += -50 /* BonusWildcard */;
                }
                segmentScores.push(subSegmentScore);
            }
            // an empty array like /home/ -> [[{home}], []]
            // if (!segment.length) pattern += '/'
            score.push(segmentScores);
        }
        // only apply the strict bonus to the last score
        if (options.strict && options.end) {
            const i = score.length - 1;
            score[i][score[i].length - 1] += 0.7000000000000001 /* BonusStrict */;
        }
        // TODO: dev only warn double trailing slash
        if (!options.strict)
            pattern += '/?';
        if (options.end)
            pattern += '$';
        // allow paths like /dynamic to only match dynamic or dynamic/... but not dynamic_something_else
        else if (options.strict)
            pattern += '(?:/|$)';
        const re = new RegExp(pattern, options.sensitive ? '' : 'i');
        function parse(path) {
            const match = path.match(re);
            const params = {};
            if (!match)
                return null;
            for (let i = 1; i < match.length; i++) {
                const value = match[i] || '';
                const key = keys[i - 1];
                params[key.name] = value && key.repeatable ? value.split('/') : value;
            }
            return params;
        }
        function stringify(params) {
            let path = '';
            // for optional parameters to allow to be empty
            let avoidDuplicatedSlash = false;
            for (const segment of segments) {
                if (!avoidDuplicatedSlash || !path.endsWith('/'))
                    path += '/';
                avoidDuplicatedSlash = false;
                for (const token of segment) {
                    if (token.type === 0 /* Static */) {
                        path += token.value;
                    }
                    else if (token.type === 1 /* Param */) {
                        const { value, repeatable, optional } = token;
                        const param = value in params ? params[value] : '';
                        if (Array.isArray(param) && !repeatable)
                            throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`);
                        const text = Array.isArray(param) ? param.join('/') : param;
                        if (!text) {
                            if (optional) {
                                // if we have more than one optional param like /:a?-static and there are more segments, we don't need to
                                // care about the optional param
                                if (segment.length < 2 && segments.length > 1) {
                                    // remove the last slash as we could be at the end
                                    if (path.endsWith('/'))
                                        path = path.slice(0, -1);
                                    // do not append a slash on the next iteration
                                    else
                                        avoidDuplicatedSlash = true;
                                }
                            }
                            else
                                throw new Error(`Missing required param "${value}"`);
                        }
                        path += text;
                    }
                }
            }
            return path;
        }
        return {
            re,
            score,
            keys,
            parse,
            stringify,
        };
    }
    /**
     * Compares an array of numbers as used in PathParser.score and returns a
     * number. This function can be used to `sort` an array
     *
     * @param a - first array of numbers
     * @param b - second array of numbers
     * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b
     * should be sorted first
     */
    function compareScoreArray(a, b) {
        let i = 0;
        while (i < a.length && i < b.length) {
            const diff = b[i] - a[i];
            // only keep going if diff === 0
            if (diff)
                return diff;
            i++;
        }
        // if the last subsegment was Static, the shorter segments should be sorted first
        // otherwise sort the longest segment first
        if (a.length < b.length) {
            return a.length === 1 && a[0] === 40 /* Static */ + 40 /* Segment */
                ? -1
                : 1;
        }
        else if (a.length > b.length) {
            return b.length === 1 && b[0] === 40 /* Static */ + 40 /* Segment */
                ? 1
                : -1;
        }
        return 0;
    }
    /**
     * Compare function that can be used with `sort` to sort an array of PathParser
     *
     * @param a - first PathParser
     * @param b - second PathParser
     * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b
     */
    function comparePathParserScore(a, b) {
        let i = 0;
        const aScore = a.score;
        const bScore = b.score;
        while (i < aScore.length && i < bScore.length) {
            const comp = compareScoreArray(aScore[i], bScore[i]);
            // do not return if both are equal
            if (comp)
                return comp;
            i++;
        }
        // if a and b share the same score entries but b has more, sort b first
        return bScore.length - aScore.length;
        // this is the ternary version
        // return aScore.length < bScore.length
        //   ? 1
        //   : aScore.length > bScore.length
        //   ? -1
        //   : 0
    }

    const ROOT_TOKEN = {
        type: 0 /* Static */,
        value: '',
    };
    const VALID_PARAM_RE = /[a-zA-Z0-9_]/;
    // After some profiling, the cache seems to be unnecessary because tokenizePath
    // (the slowest part of adding a route) is very fast
    // const tokenCache = new Map<string, Token[][]>()
    function tokenizePath(path) {
        if (!path)
            return [[]];
        if (path === '/')
            return [[ROOT_TOKEN]];
        if (!path.startsWith('/')) {
            throw new Error(`Route paths should start with a "/": "${path}" should be "/${path}".`
            );
        }
        // if (tokenCache.has(path)) return tokenCache.get(path)!
        function crash(message) {
            throw new Error(`ERR (${state})/"${buffer}": ${message}`);
        }
        let state = 0 /* Static */;
        let previousState = state;
        const tokens = [];
        // the segment will always be valid because we get into the initial state
        // with the leading /
        let segment;
        function finalizeSegment() {
            if (segment)
                tokens.push(segment);
            segment = [];
        }
        // index on the path
        let i = 0;
        // char at index
        let char;
        // buffer of the value read
        let buffer = '';
        // custom regexp for a param
        let customRe = '';
        function consumeBuffer() {
            if (!buffer)
                return;
            if (state === 0 /* Static */) {
                segment.push({
                    type: 0 /* Static */,
                    value: buffer,
                });
            }
            else if (state === 1 /* Param */ ||
                state === 2 /* ParamRegExp */ ||
                state === 3 /* ParamRegExpEnd */) {
                if (segment.length > 1 && (char === '*' || char === '+'))
                    crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`);
                segment.push({
                    type: 1 /* Param */,
                    value: buffer,
                    regexp: customRe,
                    repeatable: char === '*' || char === '+',
                    optional: char === '*' || char === '?',
                });
            }
            else {
                crash('Invalid state to consume buffer');
            }
            buffer = '';
        }
        function addCharToBuffer() {
            buffer += char;
        }
        while (i < path.length) {
            char = path[i++];
            if (char === '\\' && state !== 2 /* ParamRegExp */) {
                previousState = state;
                state = 4 /* EscapeNext */;
                continue;
            }
            switch (state) {
                case 0 /* Static */:
                    if (char === '/') {
                        if (buffer) {
                            consumeBuffer();
                        }
                        finalizeSegment();
                    }
                    else if (char === ':') {
                        consumeBuffer();
                        state = 1 /* Param */;
                    }
                    else {
                        addCharToBuffer();
                    }
                    break;
                case 4 /* EscapeNext */:
                    addCharToBuffer();
                    state = previousState;
                    break;
                case 1 /* Param */:
                    if (char === '(') {
                        state = 2 /* ParamRegExp */;
                    }
                    else if (VALID_PARAM_RE.test(char)) {
                        addCharToBuffer();
                    }
                    else {
                        consumeBuffer();
                        state = 0 /* Static */;
                        // go back one character if we were not modifying
                        if (char !== '*' && char !== '?' && char !== '+')
                            i--;
                    }
                    break;
                case 2 /* ParamRegExp */:
                    // TODO: is it worth handling nested regexp? like :p(?:prefix_([^/]+)_suffix)
                    // it already works by escaping the closing )
                    // https://paths.esm.dev/?p=AAMeJbiAwQEcDKbAoAAkP60PG2R6QAvgNaA6AFACM2ABuQBB#
                    // is this really something people need since you can also write
                    // /prefix_:p()_suffix
                    if (char === ')') {
                        // handle the escaped )
                        if (customRe[customRe.length - 1] == '\\')
                            customRe = customRe.slice(0, -1) + char;
                        else
                            state = 3 /* ParamRegExpEnd */;
                    }
                    else {
                        customRe += char;
                    }
                    break;
                case 3 /* ParamRegExpEnd */:
                    // same as finalizing a param
                    consumeBuffer();
                    state = 0 /* Static */;
                    // go back one character if we were not modifying
                    if (char !== '*' && char !== '?' && char !== '+')
                        i--;
                    customRe = '';
                    break;
                default:
                    crash('Unknown state');
                    break;
            }
        }
        if (state === 2 /* ParamRegExp */)
            crash(`Unfinished custom RegExp for param "${buffer}"`);
        consumeBuffer();
        finalizeSegment();
        // tokenCache.set(path, tokens)
        return tokens;
    }

    function createRouteRecordMatcher(record, parent, options) {
        const parser = tokensToParser(tokenizePath(record.path), options);
        // warn against params with the same name
        {
            const existingKeys = new Set();
            for (const key of parser.keys) {
                if (existingKeys.has(key.name))
                    warn(`Found duplicated params with name "${key.name}" for path "${record.path}". Only the last one will be available on "$route.params".`);
                existingKeys.add(key.name);
            }
        }
        const matcher = assign(parser, {
            record,
            parent,
            // these needs to be populated by the parent
            children: [],
            alias: [],
        });
        if (parent) {
            // both are aliases or both are not aliases
            // we don't want to mix them because the order is used when
            // passing originalRecord in Matcher.addRoute
            if (!matcher.record.aliasOf === !parent.record.aliasOf)
                parent.children.push(matcher);
        }
        return matcher;
    }

    /**
     * Creates a Router Matcher.
     *
     * @internal
     * @param routes - array of initial routes
     * @param globalOptions - global route options
     */
    function createRouterMatcher(routes, globalOptions) {
        // normalized ordered array of matchers
        const matchers = [];
        const matcherMap = new Map();
        globalOptions = mergeOptions({ strict: false, end: true, sensitive: false }, globalOptions);
        function getRecordMatcher(name) {
            return matcherMap.get(name);
        }
        function addRoute(record, parent, originalRecord) {
            // used later on to remove by name
            const isRootAdd = !originalRecord;
            const mainNormalizedRecord = normalizeRouteRecord(record);
            // we might be the child of an alias
            mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record;
            const options = mergeOptions(globalOptions, record);
            // generate an array of records to correctly handle aliases
            const normalizedRecords = [
                mainNormalizedRecord,
            ];
            if ('alias' in record) {
                const aliases = typeof record.alias === 'string' ? [record.alias] : record.alias;
                for (const alias of aliases) {
                    normalizedRecords.push(assign({}, mainNormalizedRecord, {
                        // this allows us to hold a copy of the `components` option
                        // so that async components cache is hold on the original record
                        components: originalRecord
                            ? originalRecord.record.components
                            : mainNormalizedRecord.components,
                        path: alias,
                        // we might be the child of an alias
                        aliasOf: originalRecord
                            ? originalRecord.record
                            : mainNormalizedRecord,
                        // the aliases are always of the same kind as the original since they
                        // are defined on the same record
                    }));
                }
            }
            let matcher;
            let originalMatcher;
            for (const normalizedRecord of normalizedRecords) {
                const { path } = normalizedRecord;
                // Build up the path for nested routes if the child isn't an absolute
                // route. Only add the / delimiter if the child path isn't empty and if the
                // parent path doesn't have a trailing slash
                if (parent && path[0] !== '/') {
                    const parentPath = parent.record.path;
                    const connectingSlash = parentPath[parentPath.length - 1] === '/' ? '' : '/';
                    normalizedRecord.path =
                        parent.record.path + (path && connectingSlash + path);
                }
                if (normalizedRecord.path === '*') {
                    throw new Error('Catch all routes ("*") must now be defined using a param with a custom regexp.\n' +
                        'See more at https://next.router.vuejs.org/guide/migration/#removed-star-or-catch-all-routes.');
                }
                // create the object before hand so it can be passed to children
                matcher = createRouteRecordMatcher(normalizedRecord, parent, options);
                if (parent && path[0] === '/')
                    checkMissingParamsInAbsolutePath(matcher, parent);
                // if we are an alias we must tell the original record that we exist
                // so we can be removed
                if (originalRecord) {
                    originalRecord.alias.push(matcher);
                    {
                        checkSameParams(originalRecord, matcher);
                    }
                }
                else {
                    // otherwise, the first record is the original and others are aliases
                    originalMatcher = originalMatcher || matcher;
                    if (originalMatcher !== matcher)
                        originalMatcher.alias.push(matcher);
                    // remove the route if named and only for the top record (avoid in nested calls)
                    // this works because the original record is the first one
                    if (isRootAdd && record.name && !isAliasRecord(matcher))
                        removeRoute(record.name);
                }
                if ('children' in mainNormalizedRecord) {
                    const children = mainNormalizedRecord.children;
                    for (let i = 0; i < children.length; i++) {
                        addRoute(children[i], matcher, originalRecord && originalRecord.children[i]);
                    }
                }
                // if there was no original record, then the first one was not an alias and all
                // other alias (if any) need to reference this record when adding children
                originalRecord = originalRecord || matcher;
                // TODO: add normalized records for more flexibility
                // if (parent && isAliasRecord(originalRecord)) {
                //   parent.children.push(originalRecord)
                // }
                insertMatcher(matcher);
            }
            return originalMatcher
                ? () => {
                    // since other matchers are aliases, they should be removed by the original matcher
                    removeRoute(originalMatcher);
                }
                : noop;
        }
        function removeRoute(matcherRef) {
            if (isRouteName(matcherRef)) {
                const matcher = matcherMap.get(matcherRef);
                if (matcher) {
                    matcherMap.delete(matcherRef);
                    matchers.splice(matchers.indexOf(matcher), 1);
                    matcher.children.forEach(removeRoute);
                    matcher.alias.forEach(removeRoute);
                }
            }
            else {
                const index = matchers.indexOf(matcherRef);
                if (index > -1) {
                    matchers.splice(index, 1);
                    if (matcherRef.record.name)
                        matcherMap.delete(matcherRef.record.name);
                    matcherRef.children.forEach(removeRoute);
                    matcherRef.alias.forEach(removeRoute);
                }
            }
        }
        function getRoutes() {
            return matchers;
        }
        function insertMatcher(matcher) {
            let i = 0;
            while (i < matchers.length &&
            comparePathParserScore(matcher, matchers[i]) >= 0 &&
            // Adding children with empty path should still appear before the parent
            // https://github.com/vuejs/router/issues/1124
            (matcher.record.path !== matchers[i].record.path ||
                !isRecordChildOf(matcher, matchers[i])))
                i++;
            matchers.splice(i, 0, matcher);
            // only add the original record to the name map
            if (matcher.record.name && !isAliasRecord(matcher))
                matcherMap.set(matcher.record.name, matcher);
        }
        function resolve(location, currentLocation) {
            let matcher;
            let params = {};
            let path;
            let name;
            if ('name' in location && location.name) {
                matcher = matcherMap.get(location.name);
                if (!matcher)
                    throw createRouterError(1 /* MATCHER_NOT_FOUND */, {
                        location,
                    });
                name = matcher.record.name;
                params = assign(
                    // paramsFromLocation is a new object
                    paramsFromLocation(currentLocation.params,
                        // only keep params that exist in the resolved location
                        // TODO: only keep optional params coming from a parent record
                        matcher.keys.filter(k => !k.optional).map(k => k.name)), location.params);
                // throws if cannot be stringified
                path = matcher.stringify(params);
            }
            else if ('path' in location) {
                // no need to resolve the path with the matcher as it was provided
                // this also allows the user to control the encoding
                path = location.path;
                if (!path.startsWith('/')) {
                    warn(`The Matcher cannot resolve relative paths but received "${path}". Unless you directly called \`matcher.resolve("${path}")\`, this is probably a bug in vue-router. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/router.`);
                }
                matcher = matchers.find(m => m.re.test(path));
                // matcher should have a value after the loop
                if (matcher) {
                    // TODO: dev warning of unused params if provided
                    // we know the matcher works because we tested the regexp
                    params = matcher.parse(path);
                    name = matcher.record.name;
                }
                // location is a relative path
            }
            else {
                // match by name or path of current route
                matcher = currentLocation.name
                    ? matcherMap.get(currentLocation.name)
                    : matchers.find(m => m.re.test(currentLocation.path));
                if (!matcher)
                    throw createRouterError(1 /* MATCHER_NOT_FOUND */, {
                        location,
                        currentLocation,
                    });
                name = matcher.record.name;
                // since we are navigating to the same location, we don't need to pick the
                // params like when `name` is provided
                params = assign({}, currentLocation.params, location.params);
                path = matcher.stringify(params);
            }
            const matched = [];
            let parentMatcher = matcher;
            while (parentMatcher) {
                // reversed order so parents are at the beginning
                matched.unshift(parentMatcher.record);
                parentMatcher = parentMatcher.parent;
            }
            return {
                name,
                path,
                params,
                matched,
                meta: mergeMetaFields(matched),
            };
        }
        // add initial routes
        routes.forEach(route => addRoute(route));
        return { addRoute, resolve, removeRoute, getRoutes, getRecordMatcher };
    }
    function paramsFromLocation(params, keys) {
        const newParams = {};
        for (const key of keys) {
            if (key in params)
                newParams[key] = params[key];
        }
        return newParams;
    }
    /**
     * Normalizes a RouteRecordRaw. Creates a copy
     *
     * @param record
     * @returns the normalized version
     */
    function normalizeRouteRecord(record) {
        return {
            path: record.path,
            redirect: record.redirect,
            name: record.name,
            meta: record.meta || {},
            aliasOf: undefined,
            beforeEnter: record.beforeEnter,
            props: normalizeRecordProps(record),
            children: record.children || [],
            instances: {},
            leaveGuards: new Set(),
            updateGuards: new Set(),
            enterCallbacks: {},
            components: 'components' in record
                ? record.components || {}
                : { default: record.component },
        };
    }
    /**
     * Normalize the optional `props` in a record to always be an object similar to
     * components. Also accept a boolean for components.
     * @param record
     */
    function normalizeRecordProps(record) {
        const propsObject = {};
        // props does not exist on redirect records but we can set false directly
        const props = record.props || false;
        if ('component' in record) {
            propsObject.default = props;
        }
        else {
            // NOTE: we could also allow a function to be applied to every component.
            // Would need user feedback for use cases
            for (const name in record.components)
                propsObject[name] = typeof props === 'boolean' ? props : props[name];
        }
        return propsObject;
    }
    /**
     * Checks if a record or any of its parent is an alias
     * @param record
     */
    function isAliasRecord(record) {
        while (record) {
            if (record.record.aliasOf)
                return true;
            record = record.parent;
        }
        return false;
    }
    /**
     * Merge meta fields of an array of records
     *
     * @param matched - array of matched records
     */
    function mergeMetaFields(matched) {
        return matched.reduce((meta, record) => assign(meta, record.meta), {});
    }
    function mergeOptions(defaults, partialOptions) {
        const options = {};
        for (const key in defaults) {
            options[key] = key in partialOptions ? partialOptions[key] : defaults[key];
        }
        return options;
    }
    function isSameParam(a, b) {
        return (a.name === b.name &&
            a.optional === b.optional &&
            a.repeatable === b.repeatable);
    }
    /**
     * Check if a path and its alias have the same required params
     *
     * @param a - original record
     * @param b - alias record
     */
    function checkSameParams(a, b) {
        for (const key of a.keys) {
            if (!key.optional && !b.keys.find(isSameParam.bind(null, key)))
                return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
        }
        for (const key of b.keys) {
            if (!key.optional && !a.keys.find(isSameParam.bind(null, key)))
                return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
        }
    }
    function checkMissingParamsInAbsolutePath(record, parent) {
        for (const key of parent.keys) {
            if (!record.keys.find(isSameParam.bind(null, key)))
                return warn(`Absolute path "${record.record.path}" should have the exact same param named "${key.name}" as its parent "${parent.record.path}".`);
        }
    }
    function isRecordChildOf(record, parent) {
        return parent.children.some(child => child === record || isRecordChildOf(record, child));
    }

    /**
     * Encoding Rules ␣ = Space Path: ␣ " < > # ? { } Query: ␣ " < > # & = Hash: ␣ "
     * < > `
     *
     * On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2)
     * defines some extra characters to be encoded. Most browsers do not encode them
     * in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to
     * also encode `!'()*`. Leaving unencoded only ASCII alphanumeric(`a-zA-Z0-9`)
     * plus `-._~`. This extra safety should be applied to query by patching the
     * string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\`
     * should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\`
     * into a `/` if directly typed in. The _backtick_ (`````) should also be
     * encoded everywhere because some browsers like FF encode it when directly
     * written while others don't. Safari and IE don't encode ``"<>{}``` in hash.
     */
        // const EXTRA_RESERVED_RE = /[!'()*]/g
        // const encodeReservedReplacer = (c: string) => '%' + c.charCodeAt(0).toString(16)
    const HASH_RE = /#/g; // %23
    const AMPERSAND_RE = /&/g; // %26
    const SLASH_RE = /\//g; // %2F
    const EQUAL_RE = /=/g; // %3D
    const IM_RE = /\?/g; // %3F
    const PLUS_RE = /\+/g; // %2B
    /**
     * NOTE: It's not clear to me if we should encode the + symbol in queries, it
     * seems to be less flexible than not doing so and I can't find out the legacy
     * systems requiring this for regular requests like text/html. In the standard,
     * the encoding of the plus character is only mentioned for
     * application/x-www-form-urlencoded
     * (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo
     * leave the plus character as is in queries. To be more flexible, we allow the
     * plus character on the query but it can also be manually encoded by the user.
     *
     * Resources:
     * - https://url.spec.whatwg.org/#urlencoded-parsing
     * - https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20
     */
    const ENC_BRACKET_OPEN_RE = /%5B/g; // [
    const ENC_BRACKET_CLOSE_RE = /%5D/g; // ]
    const ENC_CARET_RE = /%5E/g; // ^
    const ENC_BACKTICK_RE = /%60/g; // `
    const ENC_CURLY_OPEN_RE = /%7B/g; // {
    const ENC_PIPE_RE = /%7C/g; // |
    const ENC_CURLY_CLOSE_RE = /%7D/g; // }
    const ENC_SPACE_RE = /%20/g; // }
    /**
     * Encode characters that need to be encoded on the path, search and hash
     * sections of the URL.
     *
     * @internal
     * @param text - string to encode
     * @returns encoded string
     */
    function commonEncode(text) {
        return encodeURI('' + text)
            .replace(ENC_PIPE_RE, '|')
            .replace(ENC_BRACKET_OPEN_RE, '[')
            .replace(ENC_BRACKET_CLOSE_RE, ']');
    }
    /**
     * Encode characters that need to be encoded on the hash section of the URL.
     *
     * @param text - string to encode
     * @returns encoded string
     */
    function encodeHash(text) {
        return commonEncode(text)
            .replace(ENC_CURLY_OPEN_RE, '{')
            .replace(ENC_CURLY_CLOSE_RE, '}')
            .replace(ENC_CARET_RE, '^');
    }
    /**
     * Encode characters that need to be encoded query values on the query
     * section of the URL.
     *
     * @param text - string to encode
     * @returns encoded string
     */
    function encodeQueryValue(text) {
        return (commonEncode(text)
            // Encode the space as +, encode the + to differentiate it from the space
            .replace(PLUS_RE, '%2B')
            .replace(ENC_SPACE_RE, '+')
            .replace(HASH_RE, '%23')
            .replace(AMPERSAND_RE, '%26')
            .replace(ENC_BACKTICK_RE, '`')
            .replace(ENC_CURLY_OPEN_RE, '{')
            .replace(ENC_CURLY_CLOSE_RE, '}')
            .replace(ENC_CARET_RE, '^'));
    }
    /**
     * Like `encodeQueryValue` but also encodes the `=` character.
     *
     * @param text - string to encode
     */
    function encodeQueryKey(text) {
        return encodeQueryValue(text).replace(EQUAL_RE, '%3D');
    }
    /**
     * Encode characters that need to be encoded on the path section of the URL.
     *
     * @param text - string to encode
     * @returns encoded string
     */
    function encodePath(text) {
        return commonEncode(text).replace(HASH_RE, '%23').replace(IM_RE, '%3F');
    }
    /**
     * Encode characters that need to be encoded on the path section of the URL as a
     * param. This function encodes everything {@link encodePath} does plus the
     * slash (`/`) character. If `text` is `null` or `undefined`, returns an empty
     * string instead.
     *
     * @param text - string to encode
     * @returns encoded string
     */
    function encodeParam(text) {
        return text == null ? '' : encodePath(text).replace(SLASH_RE, '%2F');
    }
    /**
     * Decode text using `decodeURIComponent`. Returns the original text if it
     * fails.
     *
     * @param text - string to decode
     * @returns decoded string
     */
    function decode(text) {
        try {
            return decodeURIComponent('' + text);
        }
        catch (err) {
            warn(`Error decoding "${text}". Using original value`);
        }
        return '' + text;
    }

    /**
     * Transforms a queryString into a {@link LocationQuery} object. Accept both, a
     * version with the leading `?` and without Should work as URLSearchParams

     * @internal
     *
     * @param search - search string to parse
     * @returns a query object
     */
    function parseQuery(search) {
        const query = {};
        // avoid creating an object with an empty key and empty value
        // because of split('&')
        if (search === '' || search === '?')
            return query;
        const hasLeadingIM = search[0] === '?';
        const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&');
        for (let i = 0; i < searchParams.length; ++i) {
            // pre decode the + into space
            const searchParam = searchParams[i].replace(PLUS_RE, ' ');
            // allow the = character
            const eqPos = searchParam.indexOf('=');
            const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
            const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
            if (key in query) {
                // an extra variable for ts types
                let currentValue = query[key];
                if (!Array.isArray(currentValue)) {
                    currentValue = query[key] = [currentValue];
                }
                currentValue.push(value);
            }
            else {
                query[key] = value;
            }
        }
        return query;
    }
    /**
     * Stringifies a {@link LocationQueryRaw} object. Like `URLSearchParams`, it
     * doesn't prepend a `?`
     *
     * @internal
     *
     * @param query - query object to stringify
     * @returns string version of the query without the leading `?`
     */
    function stringifyQuery(query) {
        let search = '';
        for (let key in query) {
            const value = query[key];
            key = encodeQueryKey(key);
            if (value == null) {
                // only null adds the value
                if (value !== undefined) {
                    search += (search.length ? '&' : '') + key;
                }
                continue;
            }
            // keep null values
            const values = Array.isArray(value)
                ? value.map(v => v && encodeQueryValue(v))
                : [value && encodeQueryValue(value)];
            values.forEach(value => {
                // skip undefined values in arrays as if they were not present
                // smaller code than using filter
                if (value !== undefined) {
                    // only append & with non-empty search
                    search += (search.length ? '&' : '') + key;
                    if (value != null)
                        search += '=' + value;
                }
            });
        }
        return search;
    }
    /**
     * Transforms a {@link LocationQueryRaw} into a {@link LocationQuery} by casting
     * numbers into strings, removing keys with an undefined value and replacing
     * undefined with null in arrays
     *
     * @param query - query object to normalize
     * @returns a normalized query object
     */
    function normalizeQuery(query) {
        const normalizedQuery = {};
        for (const key in query) {
            const value = query[key];
            if (value !== undefined) {
                normalizedQuery[key] = Array.isArray(value)
                    ? value.map(v => (v == null ? null : '' + v))
                    : value == null
                        ? value
                        : '' + value;
            }
        }
        return normalizedQuery;
    }

    /**
     * Create a list of callbacks that can be reset. Used to create before and after navigation guards list
     */
    function useCallbacks() {
        let handlers = [];
        function add(handler) {
            handlers.push(handler);
            return () => {
                const i = handlers.indexOf(handler);
                if (i > -1)
                    handlers.splice(i, 1);
            };
        }
        function reset() {
            handlers = [];
        }
        return {
            add,
            list: () => handlers,
            reset,
        };
    }

    function registerGuard(record, name, guard) {
        const removeFromList = () => {
            record[name].delete(guard);
        };
        vue.onUnmounted(removeFromList);
        vue.onDeactivated(removeFromList);
        vue.onActivated(() => {
            record[name].add(guard);
        });
        record[name].add(guard);
    }
    /**
     * Add a navigation guard that triggers whenever the component for the current
     * location is about to be left. Similar to {@link beforeRouteLeave} but can be
     * used in any component. The guard is removed when the component is unmounted.
     *
     * @param leaveGuard - {@link NavigationGuard}
     */
    function onBeforeRouteLeave(leaveGuard) {
        if (!vue.getCurrentInstance()) {
            warn('getCurrentInstance() returned null. onBeforeRouteLeave() must be called at the top of a setup function');
            return;
        }
        const activeRecord = vue.inject(matchedRouteKey,
            // to avoid warning
            {}).value;
        if (!activeRecord) {
            warn('No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.vue?');
            return;
        }
        registerGuard(activeRecord, 'leaveGuards', leaveGuard);
    }
    /**
     * Add a navigation guard that triggers whenever the current location is about
     * to be updated. Similar to {@link beforeRouteUpdate} but can be used in any
     * component. The guard is removed when the component is unmounted.
     *
     * @param updateGuard - {@link NavigationGuard}
     */
    function onBeforeRouteUpdate(updateGuard) {
        if (!vue.getCurrentInstance()) {
            warn('getCurrentInstance() returned null. onBeforeRouteUpdate() must be called at the top of a setup function');
            return;
        }
        const activeRecord = vue.inject(matchedRouteKey,
            // to avoid warning
            {}).value;
        if (!activeRecord) {
            warn('No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.vue?');
            return;
        }
        registerGuard(activeRecord, 'updateGuards', updateGuard);
    }
    function guardToPromiseFn(guard, to, from, record, name) {
        // keep a reference to the enterCallbackArray to prevent pushing callbacks if a new navigation took place
        const enterCallbackArray = record &&
            // name is defined if record is because of the function overload
            (record.enterCallbacks[name] = record.enterCallbacks[name] || []);
        return () => new Promise((resolve, reject) => {
            const next = (valid) => {
                if (valid === false)
                    reject(createRouterError(4 /* NAVIGATION_ABORTED */, {
                        from,
                        to,
                    }));
                else if (valid instanceof Error) {
                    reject(valid);
                }
                else if (isRouteLocation(valid)) {
                    reject(createRouterError(2 /* NAVIGATION_GUARD_REDIRECT */, {
                        from: to,
                        to: valid,
                    }));
                }
                else {
                    if (enterCallbackArray &&
                        // since enterCallbackArray is truthy, both record and name also are
                        record.enterCallbacks[name] === enterCallbackArray &&
                        typeof valid === 'function')
                        enterCallbackArray.push(valid);
                    resolve();
                }
            };
            // wrapping with Promise.resolve allows it to work with both async and sync guards
            const guardReturn = guard.call(record && record.instances[name], to, from, canOnlyBeCalledOnce(next, to, from) );
            let guardCall = Promise.resolve(guardReturn);
            if (guard.length < 3)
                guardCall = guardCall.then(next);
            if (guard.length > 2) {
                const message = `The "next" callback was never called inside of ${guard.name ? '"' + guard.name + '"' : ''}:\n${guard.toString()}\n. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`;
                if (typeof guardReturn === 'object' && 'then' in guardReturn) {
                    guardCall = guardCall.then(resolvedValue => {
                        // @ts-expect-error: _called is added at canOnlyBeCalledOnce
                        if (!next._called) {
                            warn(message);
                            return Promise.reject(new Error('Invalid navigation guard'));
                        }
                        return resolvedValue;
                    });
                    // TODO: test me!
                }
                else if (guardReturn !== undefined) {
                    // @ts-expect-error: _called is added at canOnlyBeCalledOnce
                    if (!next._called) {
                        warn(message);
                        reject(new Error('Invalid navigation guard'));
                        return;
                    }
                }
            }
            guardCall.catch(err => reject(err));
        });
    }
    function canOnlyBeCalledOnce(next, to, from) {
        let called = 0;
        return function () {
            if (called++ === 1)
                warn(`The "next" callback was called more than once in one navigation guard when going from "${from.fullPath}" to "${to.fullPath}". It should be called exactly one time in each navigation guard. This will fail in production.`);
            // @ts-expect-error: we put it in the original one because it's easier to check
            next._called = true;
            if (called === 1)
                next.apply(null, arguments);
        };
    }
    function extractComponentsGuards(matched, guardType, to, from) {
        const guards = [];
        for (const record of matched) {
            for (const name in record.components) {
                let rawComponent = record.components[name];
                {
                    if (!rawComponent ||
                        (typeof rawComponent !== 'object' &&
                            typeof rawComponent !== 'function')) {
                        warn(`Component "${name}" in record with path "${record.path}" is not` +
                            ` a valid component. Received "${String(rawComponent)}".`);
                        // throw to ensure we stop here but warn to ensure the message isn't
                        // missed by the user
                        throw new Error('Invalid route component');
                    }
                    else if ('then' in rawComponent) {
                        // warn if user wrote import('/component.vue') instead of () =>
                        // import('./component.vue')
                        warn(`Component "${name}" in record with path "${record.path}" is a ` +
                            `Promise instead of a function that returns a Promise. Did you ` +
                            `write "import('./MyPage.vue')" instead of ` +
                            `"() => import('./MyPage.vue')" ? This will break in ` +
                            `production if not fixed.`);
                        const promise = rawComponent;
                        rawComponent = () => promise;
                    }
                    else if (rawComponent.__asyncLoader &&
                        // warn only once per component
                        !rawComponent.__warnedDefineAsync) {
                        rawComponent.__warnedDefineAsync = true;
                        warn(`Component "${name}" in record with path "${record.path}" is defined ` +
                            `using "defineAsyncComponent()". ` +
                            `Write "() => import('./MyPage.vue')" instead of ` +
                            `"defineAsyncComponent(() => import('./MyPage.vue'))".`);
                    }
                }
                // skip update and leave guards if the route component is not mounted
                if (guardType !== 'beforeRouteEnter' && !record.instances[name])
                    continue;
                if (isRouteComponent(rawComponent)) {
                    // __vccOpts is added by vue-class-component and contain the regular options
                    const options = rawComponent.__vccOpts || rawComponent;
                    const guard = options[guardType];
                    guard && guards.push(guardToPromiseFn(guard, to, from, record, name));
                }
                else {
                    // start requesting the chunk already
                    let componentPromise = rawComponent();
                    if (!('catch' in componentPromise)) {
                        warn(`Component "${name}" in record with path "${record.path}" is a function that does not return a Promise. If you were passing a functional component, make sure to add a "displayName" to the component. This will break in production if not fixed.`);
                        componentPromise = Promise.resolve(componentPromise);
                    }
                    guards.push(() => componentPromise.then(resolved => {
                        if (!resolved)
                            return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}"`));
                        const resolvedComponent = isESModule(resolved)
                            ? resolved.default
                            : resolved;
                        // replace the function with the resolved component
                        record.components[name] = resolvedComponent;
                        // __vccOpts is added by vue-class-component and contain the regular options
                        const options = resolvedComponent.__vccOpts || resolvedComponent;
                        const guard = options[guardType];
                        return guard && guardToPromiseFn(guard, to, from, record, name)();
                    }));
                }
            }
        }
        return guards;
    }
    /**
     * Allows differentiating lazy components from functional components and vue-class-component
     *
     * @param component
     */
    function isRouteComponent(component) {
        return (typeof component === 'object' ||
            'displayName' in component ||
            'props' in component ||
            '__vccOpts' in component);
    }

    // TODO: we could allow currentRoute as a prop to expose `isActive` and
    // `isExactActive` behavior should go through an RFC
    function useLink(props) {
        const router = vue.inject(routerKey);
        const currentRoute = vue.inject(routeLocationKey);
        const route = vue.computed(() => router.resolve(vue.unref(props.to)));
        const activeRecordIndex = vue.computed(() => {
            const { matched } = route.value;
            const { length } = matched;
            const routeMatched = matched[length - 1];
            const currentMatched = currentRoute.matched;
            if (!routeMatched || !currentMatched.length)
                return -1;
            const index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched));
            if (index > -1)
                return index;
            // possible parent record
            const parentRecordPath = getOriginalPath(matched[length - 2]);
            return (
                // we are dealing with nested routes
                length > 1 &&
                // if the parent and matched route have the same path, this link is
                // referring to the empty child. Or we currently are on a different
                // child of the same parent
                getOriginalPath(routeMatched) === parentRecordPath &&
                // avoid comparing the child with its parent
                currentMatched[currentMatched.length - 1].path !== parentRecordPath
                    ? currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2]))
                    : index);
        });
        const isActive = vue.computed(() => activeRecordIndex.value > -1 &&
            includesParams(currentRoute.params, route.value.params));
        const isExactActive = vue.computed(() => activeRecordIndex.value > -1 &&
            activeRecordIndex.value === currentRoute.matched.length - 1 &&
            isSameRouteLocationParams(currentRoute.params, route.value.params));
        function navigate(e = {}) {
            if (guardEvent(e)) {
                return router[vue.unref(props.replace) ? 'replace' : 'push'](vue.unref(props.to)
                    // avoid uncaught errors are they are logged anyway
                ).catch(noop);
            }
            return Promise.resolve();
        }
        // devtools only
        if (isBrowser) {
            const instance = vue.getCurrentInstance();
            if (instance) {
                const linkContextDevtools = {
                    route: route.value,
                    isActive: isActive.value,
                    isExactActive: isExactActive.value,
                };
                // @ts-expect-error: this is internal
                instance.__vrl_devtools = instance.__vrl_devtools || [];
                // @ts-expect-error: this is internal
                instance.__vrl_devtools.push(linkContextDevtools);
                vue.watchEffect(() => {
                    linkContextDevtools.route = route.value;
                    linkContextDevtools.isActive = isActive.value;
                    linkContextDevtools.isExactActive = isExactActive.value;
                }, { flush: 'post' });
            }
        }
        return {
            route,
            href: vue.computed(() => route.value.href),
            isActive,
            isExactActive,
            navigate,
        };
    }
    const RouterLinkImpl = /*#__PURE__*/ vue.defineComponent({
        name: 'RouterLink',
        props: {
            to: {
                type: [String, Object],
                required: true,
            },
            replace: Boolean,
            activeClass: String,
            // inactiveClass: String,
            exactActiveClass: String,
            custom: Boolean,
            ariaCurrentValue: {
                type: String,
                default: 'page',
            },
        },
        useLink,
        setup(props, { slots }) {
            const link = vue.reactive(useLink(props));
            const { options } = vue.inject(routerKey);
            const elClass = vue.computed(() => ({
                [getLinkClass(props.activeClass, options.linkActiveClass, 'router-link-active')]: link.isActive,
                // [getLinkClass(
                //   props.inactiveClass,
                //   options.linkInactiveClass,
                //   'router-link-inactive'
                // )]: !link.isExactActive,
                [getLinkClass(props.exactActiveClass, options.linkExactActiveClass, 'router-link-exact-active')]: link.isExactActive,
            }));
            return () => {
                const children = slots.default && slots.default(link);
                return props.custom
                    ? children
                    : vue.h('a', {
                        'aria-current': link.isExactActive
                            ? props.ariaCurrentValue
                            : null,
                        href: link.href,
                        // this would override user added attrs but Vue will still add
                        // the listener so we end up triggering both
                        onClick: link.navigate,
                        class: elClass.value,
                    }, children);
            };
        },
    });
    // export the public type for h/tsx inference
    // also to avoid inline import() in generated d.ts files
    /**
     * Component to render a link that triggers a navigation on click.
     */
    const RouterLink = RouterLinkImpl;
    function guardEvent(e) {
        // don't redirect with control keys
        if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
            return;
        // don't redirect when preventDefault called
        if (e.defaultPrevented)
            return;
        // don't redirect on right click
        if (e.button !== undefined && e.button !== 0)
            return;
        // don't redirect if `target="_blank"`
        // @ts-expect-error getAttribute does exist
        if (e.currentTarget && e.currentTarget.getAttribute) {
            // @ts-expect-error getAttribute exists
            const target = e.currentTarget.getAttribute('target');
            if (/\b_blank\b/i.test(target))
                return;
        }
        // this may be a Weex event which doesn't have this method
        if (e.preventDefault)
            e.preventDefault();
        return true;
    }
    function includesParams(outer, inner) {
        for (const key in inner) {
            const innerValue = inner[key];
            const outerValue = outer[key];
            if (typeof innerValue === 'string') {
                if (innerValue !== outerValue)
                    return false;
            }
            else {
                if (!Array.isArray(outerValue) ||
                    outerValue.length !== innerValue.length ||
                    innerValue.some((value, i) => value !== outerValue[i]))
                    return false;
            }
        }
        return true;
    }
    /**
     * Get the original path value of a record by following its aliasOf
     * @param record
     */
    function getOriginalPath(record) {
        return record ? (record.aliasOf ? record.aliasOf.path : record.path) : '';
    }
    /**
     * Utility class to get the active class based on defaults.
     * @param propClass
     * @param globalClass
     * @param defaultClass
     */
    const getLinkClass = (propClass, globalClass, defaultClass) => propClass != null
        ? propClass
        : globalClass != null
            ? globalClass
            : defaultClass;

    const RouterViewImpl = /*#__PURE__*/ vue.defineComponent({
        name: 'RouterView',
        // #674 we manually inherit them
        inheritAttrs: false,
        props: {
            name: {
                type: String,
                default: 'default',
            },
            route: Object,
        },
        // Better compat for @vue/compat users
        // https://github.com/vuejs/router/issues/1315
        compatConfig: { MODE: 3 },
        setup(props, { attrs, slots }) {
            warnDeprecatedUsage();
            const injectedRoute = vue.inject(routerViewLocationKey);
            const routeToDisplay = vue.computed(() => props.route || injectedRoute.value);
            const depth = vue.inject(viewDepthKey, 0);
            const matchedRouteRef = vue.computed(() => routeToDisplay.value.matched[depth]);
            vue.provide(viewDepthKey, depth + 1);
            vue.provide(matchedRouteKey, matchedRouteRef);
            vue.provide(routerViewLocationKey, routeToDisplay);
            const viewRef = vue.ref();
            // watch at the same time the component instance, the route record we are
            // rendering, and the name
            vue.watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [oldInstance, from, oldName]) => {
                // copy reused instances
                if (to) {
                    // this will update the instance for new instances as well as reused
                    // instances when navigating to a new route
                    to.instances[name] = instance;
                    // the component instance is reused for a different route or name so
                    // we copy any saved update or leave guards. With async setup, the
                    // mounting component will mount before the matchedRoute changes,
                    // making instance === oldInstance, so we check if guards have been
                    // added before. This works because we remove guards when
                    // unmounting/deactivating components
                    if (from && from !== to && instance && instance === oldInstance) {
                        if (!to.leaveGuards.size) {
                            to.leaveGuards = from.leaveGuards;
                        }
                        if (!to.updateGuards.size) {
                            to.updateGuards = from.updateGuards;
                        }
                    }
                }
                // trigger beforeRouteEnter next callbacks
                if (instance &&
                    to &&
                    // if there is no instance but to and from are the same this might be
                    // the first visit
                    (!from || !isSameRouteRecord(to, from) || !oldInstance)) {
                    (to.enterCallbacks[name] || []).forEach(callback => callback(instance));
                }
            }, { flush: 'post' });
            return () => {
                const route = routeToDisplay.value;
                const matchedRoute = matchedRouteRef.value;
                const ViewComponent = matchedRoute && matchedRoute.components[props.name];
                // we need the value at the time we render because when we unmount, we
                // navigated to a different location so the value is different
                const currentName = props.name;
                if (!ViewComponent) {
                    return normalizeSlot(slots.default, { Component: ViewComponent, route });
                }
                // props from route configuration
                const routePropsOption = matchedRoute.props[props.name];
                const routeProps = routePropsOption
                    ? routePropsOption === true
                        ? route.params
                        : typeof routePropsOption === 'function'
                            ? routePropsOption(route)
                            : routePropsOption
                    : null;
                const onVnodeUnmounted = vnode => {
                    // remove the instance reference to prevent leak
                    if (vnode.component.isUnmounted) {
                        matchedRoute.instances[currentName] = null;
                    }
                };
                const component = vue.h(ViewComponent, assign({}, routeProps, attrs, {
                    onVnodeUnmounted,
                    ref: viewRef,
                }));
                if (isBrowser &&
                    component.ref) {
                    // TODO: can display if it's an alias, its props
                    const info = {
                        depth,
                        name: matchedRoute.name,
                        path: matchedRoute.path,
                        meta: matchedRoute.meta,
                    };
                    const internalInstances = Array.isArray(component.ref)
                        ? component.ref.map(r => r.i)
                        : [component.ref.i];
                    internalInstances.forEach(instance => {
                        // @ts-expect-error
                        instance.__vrv_devtools = info;
                    });
                }
                return (
                    // pass the vnode to the slot as a prop.
                    // h and <component :is="..."> both accept vnodes
                    normalizeSlot(slots.default, { Component: component, route }) ||
                    component);
            };
        },
    });
    function normalizeSlot(slot, data) {
        if (!slot)
            return null;
        const slotContent = slot(data);
        return slotContent.length === 1 ? slotContent[0] : slotContent;
    }
    // export the public type for h/tsx inference
    // also to avoid inline import() in generated d.ts files
    /**
     * Component to display the current route the user is at.
     */
    const RouterView = RouterViewImpl;
    // warn against deprecated usage with <transition> & <keep-alive>
    // due to functional component being no longer eager in Vue 3
    function warnDeprecatedUsage() {
        const instance = vue.getCurrentInstance();
        const parentName = instance.parent && instance.parent.type.name;
        if (parentName &&
            (parentName === 'KeepAlive' || parentName.includes('Transition'))) {
            const comp = parentName === 'KeepAlive' ? 'keep-alive' : 'transition';
            warn(`<router-view> can no longer be used directly inside <transition> or <keep-alive>.\n` +
                `Use slot props instead:\n\n` +
                `<router-view v-slot="{ Component }">\n` +
                `  <${comp}>\n` +
                `    <component :is="Component" />\n` +
                `  </${comp}>\n` +
                `</router-view>`);
        }
    }

    function getDevtoolsGlobalHook() {
        return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__;
    }
    function getTarget() {
        // @ts-ignore
        return (typeof navigator !== 'undefined' && typeof window !== 'undefined')
            ? window
            : typeof global !== 'undefined'
                ? global
                : {};
    }
    const isProxyAvailable = typeof Proxy === 'function';

    const HOOK_SETUP = 'devtools-plugin:setup';
    const HOOK_PLUGIN_SETTINGS_SET = 'plugin:settings:set';

    class ApiProxy {
        constructor(plugin, hook) {
            this.target = null;
            this.targetQueue = [];
            this.onQueue = [];
            this.plugin = plugin;
            this.hook = hook;
            const defaultSettings = {};
            if (plugin.settings) {
                for (const id in plugin.settings) {
                    const item = plugin.settings[id];
                    defaultSettings[id] = item.defaultValue;
                }
            }
            const localSettingsSaveId = `__vue-devtools-plugin-settings__${plugin.id}`;
            let currentSettings = Object.assign({}, defaultSettings);
            try {
                const raw = localStorage.getItem(localSettingsSaveId);
                const data = JSON.parse(raw);
                Object.assign(currentSettings, data);
            }
            catch (e) {
                // noop
            }
            this.fallbacks = {
                getSettings() {
                    return currentSettings;
                },
                setSettings(value) {
                    try {
                        localStorage.setItem(localSettingsSaveId, JSON.stringify(value));
                    }
                    catch (e) {
                        // noop
                    }
                    currentSettings = value;
                },
            };
            if (hook) {
                hook.on(HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => {
                    if (pluginId === this.plugin.id) {
                        this.fallbacks.setSettings(value);
                    }
                });
            }
            this.proxiedOn = new Proxy({}, {
                get: (_target, prop) => {
                    if (this.target) {
                        return this.target.on[prop];
                    }
                    else {
                        return (...args) => {
                            this.onQueue.push({
                                method: prop,
                                args,
                            });
                        };
                    }
                },
            });
            this.proxiedTarget = new Proxy({}, {
                get: (_target, prop) => {
                    if (this.target) {
                        return this.target[prop];
                    }
                    else if (prop === 'on') {
                        return this.proxiedOn;
                    }
                    else if (Object.keys(this.fallbacks).includes(prop)) {
                        return (...args) => {
                            this.targetQueue.push({
                                method: prop,
                                args,
                                resolve: () => { },
                            });
                            return this.fallbacks[prop](...args);
                        };
                    }
                    else {
                        return (...args) => {
                            return new Promise(resolve => {
                                this.targetQueue.push({
                                    method: prop,
                                    args,
                                    resolve,
                                });
                            });
                        };
                    }
                },
            });
        }
        async setRealTarget(target) {
            this.target = target;
            for (const item of this.onQueue) {
                this.target.on[item.method](...item.args);
            }
            for (const item of this.targetQueue) {
                item.resolve(await this.target[item.method](...item.args));
            }
        }
    }

    function setupDevtoolsPlugin(pluginDescriptor, setupFn) {
        const descriptor = pluginDescriptor;
        const target = getTarget();
        const hook = getDevtoolsGlobalHook();
        const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy;
        if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) {
            hook.emit(HOOK_SETUP, pluginDescriptor, setupFn);
        }
        else {
            const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null;
            const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || [];
            list.push({
                pluginDescriptor: descriptor,
                setupFn,
                proxy,
            });
            if (proxy)
                setupFn(proxy.proxiedTarget);
        }
    }

    function formatRouteLocation(routeLocation, tooltip) {
        const copy = assign({}, routeLocation, {
            // remove variables that can contain vue instances
            matched: routeLocation.matched.map(matched => omit(matched, ['instances', 'children', 'aliasOf'])),
        });
        return {
            _custom: {
                type: null,
                readOnly: true,
                display: routeLocation.fullPath,
                tooltip,
                value: copy,
            },
        };
    }
    function formatDisplay(display) {
        return {
            _custom: {
                display,
            },
        };
    }
    // to support multiple router instances
    let routerId = 0;
    function addDevtools(app, router, matcher) {
        // Take over router.beforeEach and afterEach
        // make sure we are not registering the devtool twice
        if (router.__hasDevtools)
            return;
        router.__hasDevtools = true;
        // increment to support multiple router instances
        const id = routerId++;
        setupDevtoolsPlugin({
            id: 'org.vuejs.router' + (id ? '.' + id : ''),
            label: 'Vue Router',
            packageName: 'vue-router',
            homepage: 'https://router.vuejs.org',
            logo: 'https://router.vuejs.org/logo.png',
            componentStateTypes: ['Routing'],
            app,
        }, api => {
            // display state added by the router
            api.on.inspectComponent((payload, ctx) => {
                if (payload.instanceData) {
                    payload.instanceData.state.push({
                        type: 'Routing',
                        key: '$route',
                        editable: false,
                        value: formatRouteLocation(router.currentRoute.value, 'Current Route'),
                    });
                }
            });
            // mark router-link as active and display tags on router views
            api.on.visitComponentTree(({ treeNode: node, componentInstance }) => {
                if (componentInstance.__vrv_devtools) {
                    const info = componentInstance.__vrv_devtools;
                    node.tags.push({
                        label: (info.name ? `${info.name.toString()}: ` : '') + info.path,
                        textColor: 0,
                        tooltip: 'This component is rendered by &lt;router-view&gt;',
                        backgroundColor: PINK_500,
                    });
                }
                // if multiple useLink are used
                if (Array.isArray(componentInstance.__vrl_devtools)) {
                    componentInstance.__devtoolsApi = api;
                    componentInstance.__vrl_devtools.forEach(devtoolsData => {
                        let backgroundColor = ORANGE_400;
                        let tooltip = '';
                        if (devtoolsData.isExactActive) {
                            backgroundColor = LIME_500;
                            tooltip = 'This is exactly active';
                        }
                        else if (devtoolsData.isActive) {
                            backgroundColor = BLUE_600;
                            tooltip = 'This link is active';
                        }
                        node.tags.push({
                            label: devtoolsData.route.path,
                            textColor: 0,
                            tooltip,
                            backgroundColor,
                        });
                    });
                }
            });
            vue.watch(router.currentRoute, () => {
                // refresh active state
                refreshRoutesView();
                api.notifyComponentUpdate();
                api.sendInspectorTree(routerInspectorId);
                api.sendInspectorState(routerInspectorId);
            });
            const navigationsLayerId = 'router:navigations:' + id;
            api.addTimelineLayer({
                id: navigationsLayerId,
                label: `Router${id ? ' ' + id : ''} Navigations`,
                color: 0x40a8c4,
            });
            // const errorsLayerId = 'router:errors'
            // api.addTimelineLayer({
            //   id: errorsLayerId,
            //   label: 'Router Errors',
            //   color: 0xea5455,
            // })
            router.onError((error, to) => {
                api.addTimelineEvent({
                    layerId: navigationsLayerId,
                    event: {
                        title: 'Error during Navigation',
                        subtitle: to.fullPath,
                        logType: 'error',
                        time: api.now(),
                        data: { error },
                        groupId: to.meta.__navigationId,
                    },
                });
            });
            // attached to `meta` and used to group events
            let navigationId = 0;
            router.beforeEach((to, from) => {
                const data = {
                    guard: formatDisplay('beforeEach'),
                    from: formatRouteLocation(from, 'Current Location during this navigation'),
                    to: formatRouteLocation(to, 'Target location'),
                };
                // Used to group navigations together, hide from devtools
                Object.defineProperty(to.meta, '__navigationId', {
                    value: navigationId++,
                });
                api.addTimelineEvent({
                    layerId: navigationsLayerId,
                    event: {
                        time: api.now(),
                        title: 'Start of navigation',
                        subtitle: to.fullPath,
                        data,
                        groupId: to.meta.__navigationId,
                    },
                });
            });
            router.afterEach((to, from, failure) => {
                const data = {
                    guard: formatDisplay('afterEach'),
                };
                if (failure) {
                    data.failure = {
                        _custom: {
                            type: Error,
                            readOnly: true,
                            display: failure ? failure.message : '',
                            tooltip: 'Navigation Failure',
                            value: failure,
                        },
                    };
                    data.status = formatDisplay('❌');
                }
                else {
                    data.status = formatDisplay('✅');
                }
                // we set here to have the right order
                data.from = formatRouteLocation(from, 'Current Location during this navigation');
                data.to = formatRouteLocation(to, 'Target location');
                api.addTimelineEvent({
                    layerId: navigationsLayerId,
                    event: {
                        title: 'End of navigation',
                        subtitle: to.fullPath,
                        time: api.now(),
                        data,
                        logType: failure ? 'warning' : 'default',
                        groupId: to.meta.__navigationId,
                    },
                });
            });
            /**
             * Inspector of Existing routes
             */
            const routerInspectorId = 'router-inspector:' + id;
            api.addInspector({
                id: routerInspectorId,
                label: 'Routes' + (id ? ' ' + id : ''),
                icon: 'book',
                treeFilterPlaceholder: 'Search routes',
            });
            function refreshRoutesView() {
                // the routes view isn't active
                if (!activeRoutesPayload)
                    return;
                const payload = activeRoutesPayload;
                // children routes will appear as nested
                let routes = matcher.getRoutes().filter(route => !route.parent);
                // reset match state to false
                routes.forEach(resetMatchStateOnRouteRecord);
                // apply a match state if there is a payload
                if (payload.filter) {
                    routes = routes.filter(route =>
                        // save matches state based on the payload
                        isRouteMatching(route, payload.filter.toLowerCase()));
                }
                // mark active routes
                routes.forEach(route => markRouteRecordActive(route, router.currentRoute.value));
                payload.rootNodes = routes.map(formatRouteRecordForInspector);
            }
            let activeRoutesPayload;
            api.on.getInspectorTree(payload => {
                activeRoutesPayload = payload;
                if (payload.app === app && payload.inspectorId === routerInspectorId) {
                    refreshRoutesView();
                }
            });
            /**
             * Display information about the currently selected route record
             */
            api.on.getInspectorState(payload => {
                if (payload.app === app && payload.inspectorId === routerInspectorId) {
                    const routes = matcher.getRoutes();
                    const route = routes.find(route => route.record.__vd_id === payload.nodeId);
                    if (route) {
                        payload.state = {
                            options: formatRouteRecordMatcherForStateInspector(route),
                        };
                    }
                }
            });
            api.sendInspectorTree(routerInspectorId);
            api.sendInspectorState(routerInspectorId);
        });
    }
    function modifierForKey(key) {
        if (key.optional) {
            return key.repeatable ? '*' : '?';
        }
        else {
            return key.repeatable ? '+' : '';
        }
    }
    function formatRouteRecordMatcherForStateInspector(route) {
        const { record } = route;
        const fields = [
            { editable: false, key: 'path', value: record.path },
        ];
        if (record.name != null) {
            fields.push({
                editable: false,
                key: 'name',
                value: record.name,
            });
        }
        fields.push({ editable: false, key: 'regexp', value: route.re });
        if (route.keys.length) {
            fields.push({
                editable: false,
                key: 'keys',
                value: {
                    _custom: {
                        type: null,
                        readOnly: true,
                        display: route.keys
                            .map(key => `${key.name}${modifierForKey(key)}`)
                            .join(' '),
                        tooltip: 'Param keys',
                        value: route.keys,
                    },
                },
            });
        }
        if (record.redirect != null) {
            fields.push({
                editable: false,
                key: 'redirect',
                value: record.redirect,
            });
        }
        if (route.alias.length) {
            fields.push({
                editable: false,
                key: 'aliases',
                value: route.alias.map(alias => alias.record.path),
            });
        }
        fields.push({
            key: 'score',
            editable: false,
            value: {
                _custom: {
                    type: null,
                    readOnly: true,
                    display: route.score.map(score => score.join(', ')).join(' | '),
                    tooltip: 'Score used to sort routes',
                    value: route.score,
                },
            },
        });
        return fields;
    }
    /**
     * Extracted from tailwind palette
     */
    const PINK_500 = 0xec4899;
    const BLUE_600 = 0x2563eb;
    const LIME_500 = 0x84cc16;
    const CYAN_400 = 0x22d3ee;
    const ORANGE_400 = 0xfb923c;
    // const GRAY_100 = 0xf4f4f5
    const DARK = 0x666666;
    function formatRouteRecordForInspector(route) {
        const tags = [];
        const { record } = route;
        if (record.name != null) {
            tags.push({
                label: String(record.name),
                textColor: 0,
                backgroundColor: CYAN_400,
            });
        }
        if (record.aliasOf) {
            tags.push({
                label: 'alias',
                textColor: 0,
                backgroundColor: ORANGE_400,
            });
        }
        if (route.__vd_match) {
            tags.push({
                label: 'matches',
                textColor: 0,
                backgroundColor: PINK_500,
            });
        }
        if (route.__vd_exactActive) {
            tags.push({
                label: 'exact',
                textColor: 0,
                backgroundColor: LIME_500,
            });
        }
        if (route.__vd_active) {
            tags.push({
                label: 'active',
                textColor: 0,
                backgroundColor: BLUE_600,
            });
        }
        if (record.redirect) {
            tags.push({
                label: 'redirect: ' +
                    (typeof record.redirect === 'string' ? record.redirect : 'Object'),
                textColor: 0xffffff,
                backgroundColor: DARK,
            });
        }
        // add an id to be able to select it. Using the `path` is not possible because
        // empty path children would collide with their parents
        let id = record.__vd_id;
        if (id == null) {
            id = String(routeRecordId++);
            record.__vd_id = id;
        }
        return {
            id,
            label: record.path,
            tags,
            children: route.children.map(formatRouteRecordForInspector),
        };
    }
    //  incremental id for route records and inspector state
    let routeRecordId = 0;
    const EXTRACT_REGEXP_RE = /^\/(.*)\/([a-z]*)$/;
    function markRouteRecordActive(route, currentRoute) {
        // no route will be active if matched is empty
        // reset the matching state
        const isExactActive = currentRoute.matched.length &&
            isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record);
        route.__vd_exactActive = route.__vd_active = isExactActive;
        if (!isExactActive) {
            route.__vd_active = currentRoute.matched.some(match => isSameRouteRecord(match, route.record));
        }
        route.children.forEach(childRoute => markRouteRecordActive(childRoute, currentRoute));
    }
    function resetMatchStateOnRouteRecord(route) {
        route.__vd_match = false;
        route.children.forEach(resetMatchStateOnRouteRecord);
    }
    function isRouteMatching(route, filter) {
        const found = String(route.re).match(EXTRACT_REGEXP_RE);
        route.__vd_match = false;
        if (!found || found.length < 3) {
            return false;
        }
        // use a regexp without $ at the end to match nested routes better
        const nonEndingRE = new RegExp(found[1].replace(/\$$/, ''), found[2]);
        if (nonEndingRE.test(filter)) {
            // mark children as matches
            route.children.forEach(child => isRouteMatching(child, filter));
            // exception case: `/`
            if (route.record.path !== '/' || filter === '/') {
                route.__vd_match = route.re.test(filter);
                return true;
            }
            // hide the / route
            return false;
        }
        const path = route.record.path.toLowerCase();
        const decodedPath = decode(path);
        // also allow partial matching on the path
        if (!filter.startsWith('/') &&
            (decodedPath.includes(filter) || path.includes(filter)))
            return true;
        if (decodedPath.startsWith(filter) || path.startsWith(filter))
            return true;
        if (route.record.name && String(route.record.name).includes(filter))
            return true;
        return route.children.some(child => isRouteMatching(child, filter));
    }
    function omit(obj, keys) {
        const ret = {};
        for (const key in obj) {
            if (!keys.includes(key)) {
                // @ts-expect-error
                ret[key] = obj[key];
            }
        }
        return ret;
    }

    /**
     * Creates a Router instance that can be used by a Vue app.
     *
     * @param options - {@link RouterOptions}
     */
    function createRouter(options) {
        const matcher = createRouterMatcher(options.routes, options);
        const parseQuery$1 = options.parseQuery || parseQuery;
        const stringifyQuery$1 = options.stringifyQuery || stringifyQuery;
        const routerHistory = options.history;
        if (!routerHistory)
            throw new Error('Provide the "history" option when calling "createRouter()":' +
                ' https://next.router.vuejs.org/api/#history.');
        const beforeGuards = useCallbacks();
        const beforeResolveGuards = useCallbacks();
        const afterGuards = useCallbacks();
        const currentRoute = vue.shallowRef(START_LOCATION_NORMALIZED);
        let pendingLocation = START_LOCATION_NORMALIZED;
        // leave the scrollRestoration if no scrollBehavior is provided
        if (isBrowser && options.scrollBehavior && 'scrollRestoration' in history) {
            history.scrollRestoration = 'manual';
        }
        const normalizeParams = applyToParams.bind(null, paramValue => '' + paramValue);
        const encodeParams = applyToParams.bind(null, encodeParam);
        const decodeParams =
            // @ts-expect-error: intentionally avoid the type check
            applyToParams.bind(null, decode);
        function addRoute(parentOrRoute, route) {
            let parent;
            let record;
            if (isRouteName(parentOrRoute)) {
                parent = matcher.getRecordMatcher(parentOrRoute);
                record = route;
            }
            else {
                record = parentOrRoute;
            }
            return matcher.addRoute(record, parent);
        }
        function removeRoute(name) {
            const recordMatcher = matcher.getRecordMatcher(name);
            if (recordMatcher) {
                matcher.removeRoute(recordMatcher);
            }
            else {
                warn(`Cannot remove non-existent route "${String(name)}"`);
            }
        }
        function getRoutes() {
            return matcher.getRoutes().map(routeMatcher => routeMatcher.record);
        }
        function hasRoute(name) {
            return !!matcher.getRecordMatcher(name);
        }
        function resolve(rawLocation, currentLocation) {
            // const objectLocation = routerLocationAsObject(rawLocation)
            // we create a copy to modify it later
            currentLocation = assign({}, currentLocation || currentRoute.value);
            if (typeof rawLocation === 'string') {
                const locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path);
                const matchedRoute = matcher.resolve({ path: locationNormalized.path }, currentLocation);
                const href = routerHistory.createHref(locationNormalized.fullPath);
                {
                    if (href.startsWith('//'))
                        warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`);
                    else if (!matchedRoute.matched.length) {
                        warn(`No match found for location with path "${rawLocation}"`);
                    }
                }
                // locationNormalized is always a new object
                return assign(locationNormalized, matchedRoute, {
                    params: decodeParams(matchedRoute.params),
                    hash: decode(locationNormalized.hash),
                    redirectedFrom: undefined,
                    href,
                });
            }
            let matcherLocation;
            // path could be relative in object as well
            if ('path' in rawLocation) {
                if ('params' in rawLocation &&
                    !('name' in rawLocation) &&
                    // @ts-expect-error: the type is never
                    Object.keys(rawLocation.params).length) {
                    warn(`Path "${
                        // @ts-expect-error: the type is never
                        rawLocation.path}" was passed with params but they will be ignored. Use a named route alongside params instead.`);
                }
                matcherLocation = assign({}, rawLocation, {
                    path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path,
                });
            }
            else {
                // remove any nullish param
                const targetParams = assign({}, rawLocation.params);
                for (const key in targetParams) {
                    if (targetParams[key] == null) {
                        delete targetParams[key];
                    }
                }
                // pass encoded values to the matcher so it can produce encoded path and fullPath
                matcherLocation = assign({}, rawLocation, {
                    params: encodeParams(rawLocation.params),
                });
                // current location params are decoded, we need to encode them in case the
                // matcher merges the params
                currentLocation.params = encodeParams(currentLocation.params);
            }
            const matchedRoute = matcher.resolve(matcherLocation, currentLocation);
            const hash = rawLocation.hash || '';
            if (hash && !hash.startsWith('#')) {
                warn(`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`);
            }
            // decoding them) the matcher might have merged current location params so
            // we need to run the decoding again
            matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params));
            const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, {
                hash: encodeHash(hash),
                path: matchedRoute.path,
            }));
            const href = routerHistory.createHref(fullPath);
            {
                if (href.startsWith('//')) {
                    warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`);
                }
                else if (!matchedRoute.matched.length) {
                    warn(`No match found for location with path "${'path' in rawLocation ? rawLocation.path : rawLocation}"`);
                }
            }
            return assign({
                fullPath,
                // keep the hash encoded so fullPath is effectively path + encodedQuery +
                // hash
                hash,
                query:
                // if the user is using a custom query lib like qs, we might have
                // nested objects, so we keep the query as is, meaning it can contain
                // numbers at `$route.query`, but at the point, the user will have to
                // use their own type anyway.
                // https://github.com/vuejs/router/issues/328#issuecomment-649481567
                    stringifyQuery$1 === stringifyQuery
                        ? normalizeQuery(rawLocation.query)
                        : (rawLocation.query || {}),
            }, matchedRoute, {
                redirectedFrom: undefined,
                href,
            });
        }
        function locationAsObject(to) {
            return typeof to === 'string'
                ? parseURL(parseQuery$1, to, currentRoute.value.path)
                : assign({}, to);
        }
        function checkCanceledNavigation(to, from) {
            if (pendingLocation !== to) {
                return createRouterError(8 /* NAVIGATION_CANCELLED */, {
                    from,
                    to,
                });
            }
        }
        function push(to) {
            return pushWithRedirect(to);
        }
        function replace(to) {
            return push(assign(locationAsObject(to), { replace: true }));
        }
        function handleRedirectRecord(to) {
            const lastMatched = to.matched[to.matched.length - 1];
            if (lastMatched && lastMatched.redirect) {
                const { redirect } = lastMatched;
                let newTargetLocation = typeof redirect === 'function' ? redirect(to) : redirect;
                if (typeof newTargetLocation === 'string') {
                    newTargetLocation =
                        newTargetLocation.includes('?') || newTargetLocation.includes('#')
                            ? (newTargetLocation = locationAsObject(newTargetLocation))
                            : // force empty params
                            { path: newTargetLocation };
                    // @ts-expect-error: force empty params when a string is passed to let
                    // the router parse them again
                    newTargetLocation.params = {};
                }
                if (!('path' in newTargetLocation) &&
                    !('name' in newTargetLocation)) {
                    warn(`Invalid redirect found:\n${JSON.stringify(newTargetLocation, null, 2)}\n when navigating to "${to.fullPath}". A redirect must contain a name or path. This will break in production.`);
                    throw new Error('Invalid redirect');
                }
                return assign({
                    query: to.query,
                    hash: to.hash,
                    params: to.params,
                }, newTargetLocation);
            }
        }
        function pushWithRedirect(to, redirectedFrom) {
            const targetLocation = (pendingLocation = resolve(to));
            const from = currentRoute.value;
            const data = to.state;
            const force = to.force;
            // to could be a string where `replace` is a function
            const replace = to.replace === true;
            const shouldRedirect = handleRedirectRecord(targetLocation);
            if (shouldRedirect)
                return pushWithRedirect(assign(locationAsObject(shouldRedirect), {
                        state: data,
                        force,
                        replace,
                    }),
                    // keep original redirectedFrom if it exists
                    redirectedFrom || targetLocation);
            // if it was a redirect we already called `pushWithRedirect` above
            const toLocation = targetLocation;
            toLocation.redirectedFrom = redirectedFrom;
            let failure;
            if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
                failure = createRouterError(16 /* NAVIGATION_DUPLICATED */, { to: toLocation, from });
                // trigger scroll to allow scrolling to the same anchor
                handleScroll(from, from,
                    // this is a push, the only way for it to be triggered from a
                    // history.listen is with a redirect, which makes it become a push
                    true,
                    // This cannot be the first navigation because the initial location
                    // cannot be manually navigated to
                    false);
            }
            return (failure ? Promise.resolve(failure) : navigate(toLocation, from))
                .catch((error) => isNavigationFailure(error)
                    ? // navigation redirects still mark the router as ready
                    isNavigationFailure(error, 2 /* NAVIGATION_GUARD_REDIRECT */)
                        ? error
                        : markAsReady(error) // also returns the error
                    : // reject any unknown error
                    triggerError(error, toLocation, from))
                .then((failure) => {
                    if (failure) {
                        if (isNavigationFailure(failure, 2 /* NAVIGATION_GUARD_REDIRECT */)) {
                            if (// we are redirecting to the same location we were already at
                                isSameRouteLocation(stringifyQuery$1, resolve(failure.to), toLocation) &&
                                // and we have done it a couple of times
                                redirectedFrom &&
                                // @ts-expect-error: added only in dev
                                (redirectedFrom._count = redirectedFrom._count
                                    ? // @ts-expect-error
                                    redirectedFrom._count + 1
                                    : 1) > 10) {
                                warn(`Detected an infinite redirection in a navigation guard when going from "${from.fullPath}" to "${toLocation.fullPath}". Aborting to avoid a Stack Overflow. This will break in production if not fixed.`);
                                return Promise.reject(new Error('Infinite redirect in navigation guard'));
                            }
                            return pushWithRedirect(
                                // keep options
                                assign(locationAsObject(failure.to), {
                                    state: data,
                                    force,
                                    replace,
                                }),
                                // preserve the original redirectedFrom if any
                                redirectedFrom || toLocation);
                        }
                    }
                    else {
                        // if we fail we don't finalize the navigation
                        failure = finalizeNavigation(toLocation, from, true, replace, data);
                    }
                    triggerAfterEach(toLocation, from, failure);
                    return failure;
                });
        }
        /**
         * Helper to reject and skip all navigation guards if a new navigation happened
         * @param to
         * @param from
         */
        function checkCanceledNavigationAndReject(to, from) {
            const error = checkCanceledNavigation(to, from);
            return error ? Promise.reject(error) : Promise.resolve();
        }
        // TODO: refactor the whole before guards by internally using router.beforeEach
        function navigate(to, from) {
            let guards;
            const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from);
            // all components here have been resolved once because we are leaving
            guards = extractComponentsGuards(leavingRecords.reverse(), 'beforeRouteLeave', to, from);
            // leavingRecords is already reversed
            for (const record of leavingRecords) {
                record.leaveGuards.forEach(guard => {
                    guards.push(guardToPromiseFn(guard, to, from));
                });
            }
            const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from);
            guards.push(canceledNavigationCheck);
            // run the queue of per route beforeRouteLeave guards
            return (runGuardQueue(guards)
                .then(() => {
                    // check global guards beforeEach
                    guards = [];
                    for (const guard of beforeGuards.list()) {
                        guards.push(guardToPromiseFn(guard, to, from));
                    }
                    guards.push(canceledNavigationCheck);
                    return runGuardQueue(guards);
                })
                .then(() => {
                    // check in components beforeRouteUpdate
                    guards = extractComponentsGuards(updatingRecords, 'beforeRouteUpdate', to, from);
                    for (const record of updatingRecords) {
                        record.updateGuards.forEach(guard => {
                            guards.push(guardToPromiseFn(guard, to, from));
                        });
                    }
                    guards.push(canceledNavigationCheck);
                    // run the queue of per route beforeEnter guards
                    return runGuardQueue(guards);
                })
                .then(() => {
                    // check the route beforeEnter
                    guards = [];
                    for (const record of to.matched) {
                        // do not trigger beforeEnter on reused views
                        if (record.beforeEnter && !from.matched.includes(record)) {
                            if (Array.isArray(record.beforeEnter)) {
                                for (const beforeEnter of record.beforeEnter)
                                    guards.push(guardToPromiseFn(beforeEnter, to, from));
                            }
                            else {
                                guards.push(guardToPromiseFn(record.beforeEnter, to, from));
                            }
                        }
                    }
                    guards.push(canceledNavigationCheck);
                    // run the queue of per route beforeEnter guards
                    return runGuardQueue(guards);
                })
                .then(() => {
                    // NOTE: at this point to.matched is normalized and does not contain any () => Promise<Component>
                    // clear existing enterCallbacks, these are added by extractComponentsGuards
                    to.matched.forEach(record => (record.enterCallbacks = {}));
                    // check in-component beforeRouteEnter
                    guards = extractComponentsGuards(enteringRecords, 'beforeRouteEnter', to, from);
                    guards.push(canceledNavigationCheck);
                    // run the queue of per route beforeEnter guards
                    return runGuardQueue(guards);
                })
                .then(() => {
                    // check global guards beforeResolve
                    guards = [];
                    for (const guard of beforeResolveGuards.list()) {
                        guards.push(guardToPromiseFn(guard, to, from));
                    }
                    guards.push(canceledNavigationCheck);
                    return runGuardQueue(guards);
                })
                // catch any navigation canceled
                .catch(err => isNavigationFailure(err, 8 /* NAVIGATION_CANCELLED */)
                    ? err
                    : Promise.reject(err)));
        }
        function triggerAfterEach(to, from, failure) {
            // navigation is confirmed, call afterGuards
            // TODO: wrap with error handlers
            for (const guard of afterGuards.list())
                guard(to, from, failure);
        }
        /**
         * - Cleans up any navigation guards
         * - Changes the url if necessary
         * - Calls the scrollBehavior
         */
        function finalizeNavigation(toLocation, from, isPush, replace, data) {
            // a more recent navigation took place
            const error = checkCanceledNavigation(toLocation, from);
            if (error)
                return error;
            // only consider as push if it's not the first navigation
            const isFirstNavigation = from === START_LOCATION_NORMALIZED;
            const state = !isBrowser ? {} : history.state;
            // change URL only if the user did a push/replace and if it's not the initial navigation because
            // it's just reflecting the url
            if (isPush) {
                // on the initial navigation, we want to reuse the scroll position from
                // history state if it exists
                if (replace || isFirstNavigation)
                    routerHistory.replace(toLocation.fullPath, assign({
                        scroll: isFirstNavigation && state && state.scroll,
                    }, data));
                else
                    routerHistory.push(toLocation.fullPath, data);
            }
            // accept current navigation
            currentRoute.value = toLocation;
            handleScroll(toLocation, from, isPush, isFirstNavigation);
            markAsReady();
        }
        let removeHistoryListener;
        // attach listener to history to trigger navigations
        function setupListeners() {
            // avoid setting up listeners twice due to an invalid first navigation
            if (removeHistoryListener)
                return;
            removeHistoryListener = routerHistory.listen((to, _from, info) => {
                // cannot be a redirect route because it was in history
                const toLocation = resolve(to);
                // due to dynamic routing, and to hash history with manual navigation
                // (manually changing the url or calling history.hash = '#/somewhere'),
                // there could be a redirect record in history
                const shouldRedirect = handleRedirectRecord(toLocation);
                if (shouldRedirect) {
                    pushWithRedirect(assign(shouldRedirect, { replace: true }), toLocation).catch(noop);
                    return;
                }
                pendingLocation = toLocation;
                const from = currentRoute.value;
                // TODO: should be moved to web history?
                if (isBrowser) {
                    saveScrollPosition(getScrollKey(from.fullPath, info.delta), computeScrollPosition());
                }
                navigate(toLocation, from)
                    .catch((error) => {
                        if (isNavigationFailure(error, 4 /* NAVIGATION_ABORTED */ | 8 /* NAVIGATION_CANCELLED */)) {
                            return error;
                        }
                        if (isNavigationFailure(error, 2 /* NAVIGATION_GUARD_REDIRECT */)) {
                            // Here we could call if (info.delta) routerHistory.go(-info.delta,
                            // false) but this is bug prone as we have no way to wait the
                            // navigation to be finished before calling pushWithRedirect. Using
                            // a setTimeout of 16ms seems to work but there is not guarantee for
                            // it to work on every browser. So Instead we do not restore the
                            // history entry and trigger a new navigation as requested by the
                            // navigation guard.
                            // the error is already handled by router.push we just want to avoid
                            // logging the error
                            pushWithRedirect(error.to, toLocation
                                // avoid an uncaught rejection, let push call triggerError
                            )
                                .then(failure => {
                                    // manual change in hash history #916 ending up in the URL not
                                    // changing but it was changed by the manual url change, so we
                                    // need to manually change it ourselves
                                    if (isNavigationFailure(failure, 4 /* NAVIGATION_ABORTED */ |
                                            16 /* NAVIGATION_DUPLICATED */) &&
                                        !info.delta &&
                                        info.type === NavigationType.pop) {
                                        routerHistory.go(-1, false);
                                    }
                                })
                                .catch(noop);
                            // avoid the then branch
                            return Promise.reject();
                        }
                        // do not restore history on unknown direction
                        if (info.delta)
                            routerHistory.go(-info.delta, false);
                        // unrecognized error, transfer to the global handler
                        return triggerError(error, toLocation, from);
                    })
                    .then((failure) => {
                        failure =
                            failure ||
                            finalizeNavigation(
                                // after navigation, all matched components are resolved
                                toLocation, from, false);
                        // revert the navigation
                        if (failure) {
                            if (info.delta) {
                                routerHistory.go(-info.delta, false);
                            }
                            else if (info.type === NavigationType.pop &&
                                isNavigationFailure(failure, 4 /* NAVIGATION_ABORTED */ | 16 /* NAVIGATION_DUPLICATED */)) {
                                // manual change in hash history #916
                                // it's like a push but lacks the information of the direction
                                routerHistory.go(-1, false);
                            }
                        }
                        triggerAfterEach(toLocation, from, failure);
                    })
                    .catch(noop);
            });
        }
        // Initialization and Errors
        let readyHandlers = useCallbacks();
        let errorHandlers = useCallbacks();
        let ready;
        /**
         * Trigger errorHandlers added via onError and throws the error as well
         *
         * @param error - error to throw
         * @param to - location we were navigating to when the error happened
         * @param from - location we were navigating from when the error happened
         * @returns the error as a rejected promise
         */
        function triggerError(error, to, from) {
            markAsReady(error);
            const list = errorHandlers.list();
            if (list.length) {
                list.forEach(handler => handler(error, to, from));
            }
            else {
                {
                    warn('uncaught error during route navigation:');
                }
                console.error(error);
            }
            return Promise.reject(error);
        }
        function isReady() {
            if (ready && currentRoute.value !== START_LOCATION_NORMALIZED)
                return Promise.resolve();
            return new Promise((resolve, reject) => {
                readyHandlers.add([resolve, reject]);
            });
        }
        function markAsReady(err) {
            if (!ready) {
                // still not ready if an error happened
                ready = !err;
                setupListeners();
                readyHandlers
                    .list()
                    .forEach(([resolve, reject]) => (err ? reject(err) : resolve()));
                readyHandlers.reset();
            }
            return err;
        }
        // Scroll behavior
        function handleScroll(to, from, isPush, isFirstNavigation) {
            const { scrollBehavior } = options;
            if (!isBrowser || !scrollBehavior)
                return Promise.resolve();
            const scrollPosition = (!isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0))) ||
                ((isFirstNavigation || !isPush) &&
                    history.state &&
                    history.state.scroll) ||
                null;
            return vue.nextTick()
                .then(() => scrollBehavior(to, from, scrollPosition))
                .then(position => position && scrollToPosition(position))
                .catch(err => triggerError(err, to, from));
        }
        const go = (delta) => routerHistory.go(delta);
        let started;
        const installedApps = new Set();
        const router = {
            currentRoute,
            addRoute,
            removeRoute,
            hasRoute,
            getRoutes,
            resolve,
            options,
            push,
            replace,
            go,
            back: () => go(-1),
            forward: () => go(1),
            beforeEach: beforeGuards.add,
            beforeResolve: beforeResolveGuards.add,
            afterEach: afterGuards.add,
            onError: errorHandlers.add,
            isReady,
            install(app) {
                const router = this;
                app.component('RouterLink', RouterLink);
                app.component('RouterView', RouterView);
                app.config.globalProperties.$router = router;
                Object.defineProperty(app.config.globalProperties, '$route', {
                    enumerable: true,
                    get: () => vue.unref(currentRoute),
                });
                // this initial navigation is only necessary on client, on server it doesn't
                // make sense because it will create an extra unnecessary navigation and could
                // lead to problems
                if (isBrowser &&
                    // used for the initial navigation client side to avoid pushing
                    // multiple times when the router is used in multiple apps
                    !started &&
                    currentRoute.value === START_LOCATION_NORMALIZED) {
                    // see above
                    started = true;
                    push(routerHistory.location).catch(err => {
                        warn('Unexpected error when starting the router:', err);
                    });
                }
                const reactiveRoute = {};
                for (const key in START_LOCATION_NORMALIZED) {
                    // @ts-expect-error: the key matches
                    reactiveRoute[key] = vue.computed(() => currentRoute.value[key]);
                }
                app.provide(routerKey, router);
                app.provide(routeLocationKey, vue.reactive(reactiveRoute));
                app.provide(routerViewLocationKey, currentRoute);
                const unmountApp = app.unmount;
                installedApps.add(app);
                app.unmount = function () {
                    installedApps.delete(app);
                    // the router is not attached to an app anymore
                    if (installedApps.size < 1) {
                        // invalidate the current navigation
                        pendingLocation = START_LOCATION_NORMALIZED;
                        removeHistoryListener && removeHistoryListener();
                        removeHistoryListener = null;
                        currentRoute.value = START_LOCATION_NORMALIZED;
                        started = false;
                        ready = false;
                    }
                    unmountApp();
                };
                if (isBrowser) {
                    addDevtools(app, router, matcher);
                }
            },
        };
        return router;
    }
    function runGuardQueue(guards) {
        return guards.reduce((promise, guard) => promise.then(() => guard()), Promise.resolve());
    }
    function extractChangingRecords(to, from) {
        const leavingRecords = [];
        const updatingRecords = [];
        const enteringRecords = [];
        const len = Math.max(from.matched.length, to.matched.length);
        for (let i = 0; i < len; i++) {
            const recordFrom = from.matched[i];
            if (recordFrom) {
                if (to.matched.find(record => isSameRouteRecord(record, recordFrom)))
                    updatingRecords.push(recordFrom);
                else
                    leavingRecords.push(recordFrom);
            }
            const recordTo = to.matched[i];
            if (recordTo) {
                // the type doesn't matter because we are comparing per reference
                if (!from.matched.find(record => isSameRouteRecord(record, recordTo))) {
                    enteringRecords.push(recordTo);
                }
            }
        }
        return [leavingRecords, updatingRecords, enteringRecords];
    }

    /**
     * Returns the router instance. Equivalent to using `$router` inside
     * templates.
     */
    function useRouter() {
        return vue.inject(routerKey);
    }
    /**
     * Returns the current route location. Equivalent to using `$route` inside
     * templates.
     */
    function useRoute() {
        return vue.inject(routeLocationKey);
    }

    exports.RouterLink = RouterLink;
    exports.RouterView = RouterView;
    exports.START_LOCATION = START_LOCATION_NORMALIZED;
    exports.createMemoryHistory = createMemoryHistory;
    exports.createRouter = createRouter;
    exports.createRouterMatcher = createRouterMatcher;
    exports.createWebHashHistory = createWebHashHistory;
    exports.createWebHistory = createWebHistory;
    exports.isNavigationFailure = isNavigationFailure;
    exports.matchedRouteKey = matchedRouteKey;
    exports.onBeforeRouteLeave = onBeforeRouteLeave;
    exports.onBeforeRouteUpdate = onBeforeRouteUpdate;
    exports.parseQuery = parseQuery;
    exports.routeLocationKey = routeLocationKey;
    exports.routerKey = routerKey;
    exports.routerViewLocationKey = routerViewLocationKey;
    exports.stringifyQuery = stringifyQuery;
    exports.useLink = useLink;
    exports.useRoute = useRoute;
    exports.useRouter = useRouter;
    exports.viewDepthKey = viewDepthKey;

    Object.defineProperty(exports, '__esModule', { value: true });

    return exports;

})({}, Vue);